buildSDK/dependancy_bootstrap.txt
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @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         
1749         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1750             Roo.log("MenuManager hideAll");
1751             hideAll();
1752             e.stopEvent();
1753         }
1754         
1755         
1756    }
1757
1758    // private
1759    function onBeforeCheck(mi, state){
1760        if(state){
1761            var g = groups[mi.group];
1762            for(var i = 0, l = g.length; i < l; i++){
1763                if(g[i] != mi){
1764                    g[i].setChecked(false);
1765                }
1766            }
1767        }
1768    }
1769
1770    return {
1771
1772        /**
1773         * Hides all menus that are currently visible
1774         */
1775        hideAll : function(){
1776             hideAll();  
1777        },
1778
1779        // private
1780        register : function(menu){
1781            if(!menus){
1782                init();
1783            }
1784            menus[menu.id] = menu;
1785            menu.on("beforehide", onBeforeHide);
1786            menu.on("hide", onHide);
1787            menu.on("beforeshow", onBeforeShow);
1788            menu.on("show", onShow);
1789            var g = menu.group;
1790            if(g && menu.events["checkchange"]){
1791                if(!groups[g]){
1792                    groups[g] = [];
1793                }
1794                groups[g].push(menu);
1795                menu.on("checkchange", onCheck);
1796            }
1797        },
1798
1799         /**
1800          * Returns a {@link Roo.menu.Menu} object
1801          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1802          * be used to generate and return a new Menu instance.
1803          */
1804        get : function(menu){
1805            if(typeof menu == "string"){ // menu id
1806                return menus[menu];
1807            }else if(menu.events){  // menu instance
1808                return menu;
1809            }
1810            /*else if(typeof menu.length == 'number'){ // array of menu items?
1811                return new Roo.bootstrap.Menu({items:menu});
1812            }else{ // otherwise, must be a config
1813                return new Roo.bootstrap.Menu(menu);
1814            }
1815            */
1816            return false;
1817        },
1818
1819        // private
1820        unregister : function(menu){
1821            delete menus[menu.id];
1822            menu.un("beforehide", onBeforeHide);
1823            menu.un("hide", onHide);
1824            menu.un("beforeshow", onBeforeShow);
1825            menu.un("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                groups[g].remove(menu);
1829                menu.un("checkchange", onCheck);
1830            }
1831        },
1832
1833        // private
1834        registerCheckable : function(menuItem){
1835            var g = menuItem.group;
1836            if(g){
1837                if(!groups[g]){
1838                    groups[g] = [];
1839                }
1840                groups[g].push(menuItem);
1841                menuItem.on("beforecheckchange", onBeforeCheck);
1842            }
1843        },
1844
1845        // private
1846        unregisterCheckable : function(menuItem){
1847            var g = menuItem.group;
1848            if(g){
1849                groups[g].remove(menuItem);
1850                menuItem.un("beforecheckchange", onBeforeCheck);
1851            }
1852        }
1853    };
1854 }();/*
1855  * - LGPL
1856  *
1857  * menu
1858  * 
1859  */
1860
1861 /**
1862  * @class Roo.bootstrap.Menu
1863  * @extends Roo.bootstrap.Component
1864  * Bootstrap Menu class - container for MenuItems
1865  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1866  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1867  * 
1868  * @constructor
1869  * Create a new Menu
1870  * @param {Object} config The config object
1871  */
1872
1873
1874 Roo.bootstrap.Menu = function(config){
1875     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1876     if (this.registerMenu && this.type != 'treeview')  {
1877         Roo.bootstrap.MenuMgr.register(this);
1878     }
1879     this.addEvents({
1880         /**
1881          * @event beforeshow
1882          * Fires before this menu is displayed
1883          * @param {Roo.menu.Menu} this
1884          */
1885         beforeshow : true,
1886         /**
1887          * @event beforehide
1888          * Fires before this menu is hidden
1889          * @param {Roo.menu.Menu} this
1890          */
1891         beforehide : true,
1892         /**
1893          * @event show
1894          * Fires after this menu is displayed
1895          * @param {Roo.menu.Menu} this
1896          */
1897         show : true,
1898         /**
1899          * @event hide
1900          * Fires after this menu is hidden
1901          * @param {Roo.menu.Menu} this
1902          */
1903         hide : true,
1904         /**
1905          * @event click
1906          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1907          * @param {Roo.menu.Menu} this
1908          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1909          * @param {Roo.EventObject} e
1910          */
1911         click : true,
1912         /**
1913          * @event mouseover
1914          * Fires when the mouse is hovering over this menu
1915          * @param {Roo.menu.Menu} this
1916          * @param {Roo.EventObject} e
1917          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1918          */
1919         mouseover : true,
1920         /**
1921          * @event mouseout
1922          * Fires when the mouse exits this menu
1923          * @param {Roo.menu.Menu} this
1924          * @param {Roo.EventObject} e
1925          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1926          */
1927         mouseout : true,
1928         /**
1929          * @event itemclick
1930          * Fires when a menu item contained in this menu is clicked
1931          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1932          * @param {Roo.EventObject} e
1933          */
1934         itemclick: true
1935     });
1936     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1937 };
1938
1939 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1940     
1941    /// html : false,
1942     //align : '',
1943     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1944     type: false,
1945     /**
1946      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1947      */
1948     registerMenu : true,
1949     
1950     menuItems :false, // stores the menu items..
1951     
1952     hidden:true,
1953     
1954         
1955     parentMenu : false,
1956     
1957     getChildContainer : function() {
1958         return this.el;  
1959     },
1960     
1961     getAutoCreate : function(){
1962          
1963         //if (['right'].indexOf(this.align)!==-1) {
1964         //    cfg.cn[1].cls += ' pull-right'
1965         //}
1966         
1967         
1968         var cfg = {
1969             tag : 'ul',
1970             cls : 'dropdown-menu' ,
1971             style : 'z-index:1000'
1972             
1973         };
1974         
1975         if (this.type === 'submenu') {
1976             cfg.cls = 'submenu active';
1977         }
1978         if (this.type === 'treeview') {
1979             cfg.cls = 'treeview-menu';
1980         }
1981         
1982         return cfg;
1983     },
1984     initEvents : function() {
1985         
1986        // Roo.log("ADD event");
1987        // Roo.log(this.triggerEl.dom);
1988         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1989         
1990         this.triggerEl.addClass('dropdown-toggle');
1991         
1992         
1993         if (Roo.isTouch) {
1994             this.el.on('touchstart'  , this.onTouch, this);
1995         }
1996         this.el.on('click' , this.onClick, this);
1997
1998         this.el.on("mouseover", this.onMouseOver, this);
1999         this.el.on("mouseout", this.onMouseOut, this);
2000         
2001     },
2002     
2003     findTargetItem : function(e)
2004     {
2005         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2006         if(!t){
2007             return false;
2008         }
2009         //Roo.log(t);         Roo.log(t.id);
2010         if(t && t.id){
2011             //Roo.log(this.menuitems);
2012             return this.menuitems.get(t.id);
2013             
2014             //return this.items.get(t.menuItemId);
2015         }
2016         
2017         return false;
2018     },
2019     
2020     onTouch : function(e) 
2021     {
2022         //e.stopEvent(); this make the user popdown broken
2023         this.onClick(e);
2024     },
2025     
2026     onClick : function(e)
2027     {
2028         Roo.log("menu.onClick");
2029         var t = this.findTargetItem(e);
2030         if(!t || t.isContainer){
2031             return;
2032         }
2033         Roo.log(e);
2034         /*
2035         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2036             if(t == this.activeItem && t.shouldDeactivate(e)){
2037                 this.activeItem.deactivate();
2038                 delete this.activeItem;
2039                 return;
2040             }
2041             if(t.canActivate){
2042                 this.setActiveItem(t, true);
2043             }
2044             return;
2045             
2046             
2047         }
2048         */
2049        
2050         Roo.log('pass click event');
2051         
2052         t.onClick(e);
2053         
2054         this.fireEvent("click", this, t, e);
2055         
2056         this.hide();
2057     },
2058      onMouseOver : function(e){
2059         var t  = this.findTargetItem(e);
2060         //Roo.log(t);
2061         //if(t){
2062         //    if(t.canActivate && !t.disabled){
2063         //        this.setActiveItem(t, true);
2064         //    }
2065         //}
2066         
2067         this.fireEvent("mouseover", this, e, t);
2068     },
2069     isVisible : function(){
2070         return !this.hidden;
2071     },
2072      onMouseOut : function(e){
2073         var t  = this.findTargetItem(e);
2074         
2075         //if(t ){
2076         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2077         //        this.activeItem.deactivate();
2078         //        delete this.activeItem;
2079         //    }
2080         //}
2081         this.fireEvent("mouseout", this, e, t);
2082     },
2083     
2084     
2085     /**
2086      * Displays this menu relative to another element
2087      * @param {String/HTMLElement/Roo.Element} element The element to align to
2088      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2089      * the element (defaults to this.defaultAlign)
2090      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2091      */
2092     show : function(el, pos, parentMenu){
2093         this.parentMenu = parentMenu;
2094         if(!this.el){
2095             this.render();
2096         }
2097         this.fireEvent("beforeshow", this);
2098         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2099     },
2100      /**
2101      * Displays this menu at a specific xy position
2102      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2103      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2104      */
2105     showAt : function(xy, parentMenu, /* private: */_e){
2106         this.parentMenu = parentMenu;
2107         if(!this.el){
2108             this.render();
2109         }
2110         if(_e !== false){
2111             this.fireEvent("beforeshow", this);
2112             //xy = this.el.adjustForConstraints(xy);
2113         }
2114         
2115         //this.el.show();
2116         this.hideMenuItems();
2117         this.hidden = false;
2118         this.triggerEl.addClass('open');
2119         
2120         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2121             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2122         }
2123         
2124         this.el.setXY(xy);
2125         this.focus();
2126         this.fireEvent("show", this);
2127     },
2128     
2129     focus : function(){
2130         return;
2131         if(!this.hidden){
2132             this.doFocus.defer(50, this);
2133         }
2134     },
2135
2136     doFocus : function(){
2137         if(!this.hidden){
2138             this.focusEl.focus();
2139         }
2140     },
2141
2142     /**
2143      * Hides this menu and optionally all parent menus
2144      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2145      */
2146     hide : function(deep)
2147     {
2148         
2149         this.hideMenuItems();
2150         if(this.el && this.isVisible()){
2151             this.fireEvent("beforehide", this);
2152             if(this.activeItem){
2153                 this.activeItem.deactivate();
2154                 this.activeItem = null;
2155             }
2156             this.triggerEl.removeClass('open');;
2157             this.hidden = true;
2158             this.fireEvent("hide", this);
2159         }
2160         if(deep === true && this.parentMenu){
2161             this.parentMenu.hide(true);
2162         }
2163     },
2164     
2165     onTriggerPress  : function(e)
2166     {
2167         
2168         Roo.log('trigger press');
2169         //Roo.log(e.getTarget());
2170        // Roo.log(this.triggerEl.dom);
2171        
2172         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2173         var pel = Roo.get(e.getTarget());
2174         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2175            
2176             return;
2177         }
2178         
2179         if (this.isVisible()) {
2180             Roo.log('hide');
2181             this.hide();
2182         } else {
2183             Roo.log('show');
2184             this.show(this.triggerEl, false, false);
2185         }
2186         
2187         e.stopEvent();
2188     },
2189     
2190          
2191        
2192     
2193     hideMenuItems : function()
2194     {
2195         Roo.log("hide Menu Items");
2196         if (!this.el) { 
2197             return;
2198         }
2199         //$(backdrop).remove()
2200         this.el.select('.open',true).each(function(aa) {
2201             
2202             aa.removeClass('open');
2203           //var parent = getParent($(this))
2204           //var relatedTarget = { relatedTarget: this }
2205           
2206            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2207           //if (e.isDefaultPrevented()) return
2208            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2209         });
2210     },
2211     addxtypeChild : function (tree, cntr) {
2212         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2213           
2214         this.menuitems.add(comp);
2215         return comp;
2216
2217     },
2218     getEl : function()
2219     {
2220         Roo.log(this.el);
2221         return this.el;
2222     }
2223 });
2224
2225  
2226  /*
2227  * - LGPL
2228  *
2229  * menu item
2230  * 
2231  */
2232
2233
2234 /**
2235  * @class Roo.bootstrap.MenuItem
2236  * @extends Roo.bootstrap.Component
2237  * Bootstrap MenuItem class
2238  * @cfg {String} html the menu label
2239  * @cfg {String} href the link
2240  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2241  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2242  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2243  * @cfg {String} fa favicon to show on left of menu item.
2244  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2245  * 
2246  * 
2247  * @constructor
2248  * Create a new MenuItem
2249  * @param {Object} config The config object
2250  */
2251
2252
2253 Roo.bootstrap.MenuItem = function(config){
2254     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2255     this.addEvents({
2256         // raw events
2257         /**
2258          * @event click
2259          * The raw click event for the entire grid.
2260          * @param {Roo.bootstrap.MenuItem} this
2261          * @param {Roo.EventObject} e
2262          */
2263         "click" : true
2264     });
2265 };
2266
2267 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2268     
2269     href : false,
2270     html : false,
2271     preventDefault: true,
2272     isContainer : false,
2273     active : false,
2274     fa: false,
2275     
2276     getAutoCreate : function(){
2277         
2278         if(this.isContainer){
2279             return {
2280                 tag: 'li',
2281                 cls: 'dropdown-menu-item'
2282             };
2283         }
2284         var ctag = {
2285             tag: 'span',
2286             html: 'Link'
2287         };
2288         
2289         var anc = {
2290             tag : 'a',
2291             href : '#',
2292             cn : [  ]
2293         };
2294         
2295         if (this.fa !== false) {
2296             anc.cn.push({
2297                 tag : 'i',
2298                 cls : 'fa fa-' + this.fa
2299             });
2300         }
2301         
2302         anc.cn.push(ctag);
2303         
2304         
2305         var cfg= {
2306             tag: 'li',
2307             cls: 'dropdown-menu-item',
2308             cn: [ anc ]
2309         };
2310         if (this.parent().type == 'treeview') {
2311             cfg.cls = 'treeview-menu';
2312         }
2313         if (this.active) {
2314             cfg.cls += ' active';
2315         }
2316         
2317         
2318         
2319         anc.href = this.href || cfg.cn[0].href ;
2320         ctag.html = this.html || cfg.cn[0].html ;
2321         return cfg;
2322     },
2323     
2324     initEvents: function()
2325     {
2326         if (this.parent().type == 'treeview') {
2327             this.el.select('a').on('click', this.onClick, this);
2328         }
2329         if (this.menu) {
2330             this.menu.parentType = this.xtype;
2331             this.menu.triggerEl = this.el;
2332             this.menu = this.addxtype(Roo.apply({}, this.menu));
2333         }
2334         
2335     },
2336     onClick : function(e)
2337     {
2338         Roo.log('item on click ');
2339         //if(this.preventDefault){
2340         //    e.preventDefault();
2341         //}
2342         //this.parent().hideMenuItems();
2343         
2344         this.fireEvent('click', this, e);
2345     },
2346     getEl : function()
2347     {
2348         return this.el;
2349     }
2350 });
2351
2352  
2353
2354  /*
2355  * - LGPL
2356  *
2357  * menu separator
2358  * 
2359  */
2360
2361
2362 /**
2363  * @class Roo.bootstrap.MenuSeparator
2364  * @extends Roo.bootstrap.Component
2365  * Bootstrap MenuSeparator class
2366  * 
2367  * @constructor
2368  * Create a new MenuItem
2369  * @param {Object} config The config object
2370  */
2371
2372
2373 Roo.bootstrap.MenuSeparator = function(config){
2374     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2375 };
2376
2377 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2378     
2379     getAutoCreate : function(){
2380         var cfg = {
2381             cls: 'divider',
2382             tag : 'li'
2383         };
2384         
2385         return cfg;
2386     }
2387    
2388 });
2389
2390  
2391
2392  
2393 /*
2394 * Licence: LGPL
2395 */
2396
2397 /**
2398  * @class Roo.bootstrap.Modal
2399  * @extends Roo.bootstrap.Component
2400  * Bootstrap Modal class
2401  * @cfg {String} title Title of dialog
2402  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2403  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2404  * @cfg {Boolean} specificTitle default false
2405  * @cfg {Array} buttons Array of buttons or standard button set..
2406  * @cfg {String} buttonPosition (left|right|center) default right
2407  * @cfg {Boolean} animate default true
2408  * @cfg {Boolean} allow_close default true
2409  * 
2410  * @constructor
2411  * Create a new Modal Dialog
2412  * @param {Object} config The config object
2413  */
2414
2415 Roo.bootstrap.Modal = function(config){
2416     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2417     this.addEvents({
2418         // raw events
2419         /**
2420          * @event btnclick
2421          * The raw btnclick event for the button
2422          * @param {Roo.EventObject} e
2423          */
2424         "btnclick" : true
2425     });
2426     this.buttons = this.buttons || [];
2427      
2428     if (this.tmpl) {
2429         this.tmpl = Roo.factory(this.tmpl);
2430     }
2431     
2432 };
2433
2434 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2435     
2436     title : 'test dialog',
2437    
2438     buttons : false,
2439     
2440     // set on load...
2441      
2442     html: false,
2443     
2444     tmp: false,
2445     
2446     specificTitle: false,
2447     
2448     buttonPosition: 'right',
2449     
2450     allow_close : true,
2451     
2452     animate : true,
2453     
2454     
2455      // private
2456     bodyEl:  false,
2457     footerEl:  false,
2458     titleEl:  false,
2459     closeEl:  false,
2460     
2461     
2462     onRender : function(ct, position)
2463     {
2464         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2465      
2466         if(!this.el){
2467             var cfg = Roo.apply({},  this.getAutoCreate());
2468             cfg.id = Roo.id();
2469             //if(!cfg.name){
2470             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2471             //}
2472             //if (!cfg.name.length) {
2473             //    delete cfg.name;
2474            // }
2475             if (this.cls) {
2476                 cfg.cls += ' ' + this.cls;
2477             }
2478             if (this.style) {
2479                 cfg.style = this.style;
2480             }
2481             this.el = Roo.get(document.body).createChild(cfg, position);
2482         }
2483         //var type = this.el.dom.type;
2484         
2485         
2486         if(this.tabIndex !== undefined){
2487             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2488         }
2489         
2490         
2491         this.bodyEl = this.el.select('.modal-body',true).first();
2492         this.closeEl = this.el.select('.modal-header .close', true).first();
2493         this.footerEl = this.el.select('.modal-footer',true).first();
2494         this.titleEl = this.el.select('.modal-title',true).first();
2495         
2496         
2497          
2498         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2499         this.maskEl.enableDisplayMode("block");
2500         this.maskEl.hide();
2501         //this.el.addClass("x-dlg-modal");
2502     
2503         if (this.buttons.length) {
2504             Roo.each(this.buttons, function(bb) {
2505                 var b = Roo.apply({}, bb);
2506                 b.xns = b.xns || Roo.bootstrap;
2507                 b.xtype = b.xtype || 'Button';
2508                 if (typeof(b.listeners) == 'undefined') {
2509                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2510                 }
2511                 
2512                 var btn = Roo.factory(b);
2513                 
2514                 btn.render(this.el.select('.modal-footer div').first());
2515                 
2516             },this);
2517         }
2518         // render the children.
2519         var nitems = [];
2520         
2521         if(typeof(this.items) != 'undefined'){
2522             var items = this.items;
2523             delete this.items;
2524
2525             for(var i =0;i < items.length;i++) {
2526                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2527             }
2528         }
2529         
2530         this.items = nitems;
2531         
2532         // where are these used - they used to be body/close/footer
2533         
2534        
2535         this.initEvents();
2536         //this.el.addClass([this.fieldClass, this.cls]);
2537         
2538     },
2539     
2540     getAutoCreate : function(){
2541         
2542         
2543         var bdy = {
2544                 cls : 'modal-body',
2545                 html : this.html || ''
2546         };
2547         
2548         var title = {
2549             tag: 'h4',
2550             cls : 'modal-title',
2551             html : this.title
2552         };
2553         
2554         if(this.specificTitle){
2555             title = this.title;
2556             
2557         };
2558         
2559         var header = [];
2560         if (this.allow_close) {
2561             header.push({
2562                 tag: 'button',
2563                 cls : 'close',
2564                 html : '&times'
2565             });
2566         }
2567         header.push(title);
2568         
2569         var modal = {
2570             cls: "modal",
2571             style : 'display: none',
2572             cn : [
2573                 {
2574                     cls: "modal-dialog",
2575                     cn : [
2576                         {
2577                             cls : "modal-content",
2578                             cn : [
2579                                 {
2580                                     cls : 'modal-header',
2581                                     cn : header
2582                                 },
2583                                 bdy,
2584                                 {
2585                                     cls : 'modal-footer',
2586                                     cn : [
2587                                         {
2588                                             tag: 'div',
2589                                             cls: 'btn-' + this.buttonPosition
2590                                         }
2591                                     ]
2592                                     
2593                                 }
2594                                 
2595                                 
2596                             ]
2597                             
2598                         }
2599                     ]
2600                         
2601                 }
2602             ]
2603         };
2604         
2605         if(this.animate){
2606             modal.cls += ' fade';
2607         }
2608         
2609         return modal;
2610           
2611     },
2612     getChildContainer : function() {
2613          
2614          return this.bodyEl;
2615         
2616     },
2617     getButtonContainer : function() {
2618          return this.el.select('.modal-footer div',true).first();
2619         
2620     },
2621     initEvents : function()
2622     {
2623         if (this.allow_close) {
2624             this.closeEl.on('click', this.hide, this);
2625         }
2626         
2627         var _this = this;
2628         
2629         window.addEventListener("resize", function() { _this.resize(); } );
2630
2631     },
2632     
2633     resize : function()
2634     {
2635         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2636     },
2637     
2638     show : function() {
2639         
2640         if (!this.rendered) {
2641             this.render();
2642         }
2643         
2644         this.el.setStyle('display', 'block');
2645         
2646         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2647             var _this = this;
2648             (function(){
2649                 this.el.addClass('in');
2650             }).defer(50, this);
2651         }else{
2652             this.el.addClass('in');
2653             
2654         }
2655         
2656         // not sure how we can show data in here.. 
2657         //if (this.tmpl) {
2658         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2659         //}
2660         
2661         Roo.get(document.body).addClass("x-body-masked");
2662         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2663         this.maskEl.show();
2664         this.el.setStyle('zIndex', '10001');
2665        
2666         this.fireEvent('show', this);
2667          
2668         
2669         
2670     },
2671     hide : function()
2672     {
2673         this.maskEl.hide();
2674         Roo.get(document.body).removeClass("x-body-masked");
2675         this.el.removeClass('in');
2676         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2677         
2678         if(this.animate){ // why
2679             var _this = this;
2680             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2681         }else{
2682             this.el.setStyle('display', 'none');
2683         }
2684         
2685         this.fireEvent('hide', this);
2686     },
2687     
2688     addButton : function(str, cb)
2689     {
2690          
2691         
2692         var b = Roo.apply({}, { html : str } );
2693         b.xns = b.xns || Roo.bootstrap;
2694         b.xtype = b.xtype || 'Button';
2695         if (typeof(b.listeners) == 'undefined') {
2696             b.listeners = { click : cb.createDelegate(this)  };
2697         }
2698         
2699         var btn = Roo.factory(b);
2700            
2701         btn.render(this.el.select('.modal-footer div').first());
2702         
2703         return btn;   
2704        
2705     },
2706     
2707     setDefaultButton : function(btn)
2708     {
2709         //this.el.select('.modal-footer').()
2710     },
2711     diff : false,
2712     
2713     resizeTo: function(w,h)
2714     {
2715         // skip.. ?? why??
2716         
2717         this.el.select('.modal-dialog',true).first().setWidth(w);
2718         if (this.diff === false) {
2719             this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2720         }
2721         
2722         this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2723         
2724         
2725     },
2726     setContentSize  : function(w, h)
2727     {
2728         
2729     },
2730     onButtonClick: function(btn,e)
2731     {
2732         //Roo.log([a,b,c]);
2733         this.fireEvent('btnclick', btn.name, e);
2734     },
2735      /**
2736      * Set the title of the Dialog
2737      * @param {String} str new Title
2738      */
2739     setTitle: function(str) {
2740         this.titleEl.dom.innerHTML = str;    
2741     },
2742     /**
2743      * Set the body of the Dialog
2744      * @param {String} str new Title
2745      */
2746     setBody: function(str) {
2747         this.bodyEl.dom.innerHTML = str;    
2748     },
2749     /**
2750      * Set the body of the Dialog using the template
2751      * @param {Obj} data - apply this data to the template and replace the body contents.
2752      */
2753     applyBody: function(obj)
2754     {
2755         if (!this.tmpl) {
2756             Roo.log("Error - using apply Body without a template");
2757             //code
2758         }
2759         this.tmpl.overwrite(this.bodyEl, obj);
2760     }
2761     
2762 });
2763
2764
2765 Roo.apply(Roo.bootstrap.Modal,  {
2766     /**
2767          * Button config that displays a single OK button
2768          * @type Object
2769          */
2770         OK :  [{
2771             name : 'ok',
2772             weight : 'primary',
2773             html : 'OK'
2774         }], 
2775         /**
2776          * Button config that displays Yes and No buttons
2777          * @type Object
2778          */
2779         YESNO : [
2780             {
2781                 name  : 'no',
2782                 html : 'No'
2783             },
2784             {
2785                 name  :'yes',
2786                 weight : 'primary',
2787                 html : 'Yes'
2788             }
2789         ],
2790         
2791         /**
2792          * Button config that displays OK and Cancel buttons
2793          * @type Object
2794          */
2795         OKCANCEL : [
2796             {
2797                name : 'cancel',
2798                 html : 'Cancel'
2799             },
2800             {
2801                 name : 'ok',
2802                 weight : 'primary',
2803                 html : 'OK'
2804             }
2805         ],
2806         /**
2807          * Button config that displays Yes, No and Cancel buttons
2808          * @type Object
2809          */
2810         YESNOCANCEL : [
2811             {
2812                 name : 'yes',
2813                 weight : 'primary',
2814                 html : 'Yes'
2815             },
2816             {
2817                 name : 'no',
2818                 html : 'No'
2819             },
2820             {
2821                 name : 'cancel',
2822                 html : 'Cancel'
2823             }
2824         ]
2825 });
2826  
2827  /*
2828  * - LGPL
2829  *
2830  * messagebox - can be used as a replace
2831  * 
2832  */
2833 /**
2834  * @class Roo.MessageBox
2835  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2836  * Example usage:
2837  *<pre><code>
2838 // Basic alert:
2839 Roo.Msg.alert('Status', 'Changes saved successfully.');
2840
2841 // Prompt for user data:
2842 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2843     if (btn == 'ok'){
2844         // process text value...
2845     }
2846 });
2847
2848 // Show a dialog using config options:
2849 Roo.Msg.show({
2850    title:'Save Changes?',
2851    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2852    buttons: Roo.Msg.YESNOCANCEL,
2853    fn: processResult,
2854    animEl: 'elId'
2855 });
2856 </code></pre>
2857  * @singleton
2858  */
2859 Roo.bootstrap.MessageBox = function(){
2860     var dlg, opt, mask, waitTimer;
2861     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2862     var buttons, activeTextEl, bwidth;
2863
2864     
2865     // private
2866     var handleButton = function(button){
2867         dlg.hide();
2868         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2869     };
2870
2871     // private
2872     var handleHide = function(){
2873         if(opt && opt.cls){
2874             dlg.el.removeClass(opt.cls);
2875         }
2876         //if(waitTimer){
2877         //    Roo.TaskMgr.stop(waitTimer);
2878         //    waitTimer = null;
2879         //}
2880     };
2881
2882     // private
2883     var updateButtons = function(b){
2884         var width = 0;
2885         if(!b){
2886             buttons["ok"].hide();
2887             buttons["cancel"].hide();
2888             buttons["yes"].hide();
2889             buttons["no"].hide();
2890             //dlg.footer.dom.style.display = 'none';
2891             return width;
2892         }
2893         dlg.footerEl.dom.style.display = '';
2894         for(var k in buttons){
2895             if(typeof buttons[k] != "function"){
2896                 if(b[k]){
2897                     buttons[k].show();
2898                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2899                     width += buttons[k].el.getWidth()+15;
2900                 }else{
2901                     buttons[k].hide();
2902                 }
2903             }
2904         }
2905         return width;
2906     };
2907
2908     // private
2909     var handleEsc = function(d, k, e){
2910         if(opt && opt.closable !== false){
2911             dlg.hide();
2912         }
2913         if(e){
2914             e.stopEvent();
2915         }
2916     };
2917
2918     return {
2919         /**
2920          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2921          * @return {Roo.BasicDialog} The BasicDialog element
2922          */
2923         getDialog : function(){
2924            if(!dlg){
2925                 dlg = new Roo.bootstrap.Modal( {
2926                     //draggable: true,
2927                     //resizable:false,
2928                     //constraintoviewport:false,
2929                     //fixedcenter:true,
2930                     //collapsible : false,
2931                     //shim:true,
2932                     //modal: true,
2933                   //  width:400,
2934                   //  height:100,
2935                     //buttonAlign:"center",
2936                     closeClick : function(){
2937                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2938                             handleButton("no");
2939                         }else{
2940                             handleButton("cancel");
2941                         }
2942                     }
2943                 });
2944                 dlg.render();
2945                 dlg.on("hide", handleHide);
2946                 mask = dlg.mask;
2947                 //dlg.addKeyListener(27, handleEsc);
2948                 buttons = {};
2949                 this.buttons = buttons;
2950                 var bt = this.buttonText;
2951                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2952                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2953                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2954                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2955                 //Roo.log(buttons);
2956                 bodyEl = dlg.bodyEl.createChild({
2957
2958                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2959                         '<textarea class="roo-mb-textarea"></textarea>' +
2960                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2961                 });
2962                 msgEl = bodyEl.dom.firstChild;
2963                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2964                 textboxEl.enableDisplayMode();
2965                 textboxEl.addKeyListener([10,13], function(){
2966                     if(dlg.isVisible() && opt && opt.buttons){
2967                         if(opt.buttons.ok){
2968                             handleButton("ok");
2969                         }else if(opt.buttons.yes){
2970                             handleButton("yes");
2971                         }
2972                     }
2973                 });
2974                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2975                 textareaEl.enableDisplayMode();
2976                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2977                 progressEl.enableDisplayMode();
2978                 var pf = progressEl.dom.firstChild;
2979                 if (pf) {
2980                     pp = Roo.get(pf.firstChild);
2981                     pp.setHeight(pf.offsetHeight);
2982                 }
2983                 
2984             }
2985             return dlg;
2986         },
2987
2988         /**
2989          * Updates the message box body text
2990          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2991          * the XHTML-compliant non-breaking space character '&amp;#160;')
2992          * @return {Roo.MessageBox} This message box
2993          */
2994         updateText : function(text){
2995             if(!dlg.isVisible() && !opt.width){
2996                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2997             }
2998             msgEl.innerHTML = text || '&#160;';
2999       
3000             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3001             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3002             var w = Math.max(
3003                     Math.min(opt.width || cw , this.maxWidth), 
3004                     Math.max(opt.minWidth || this.minWidth, bwidth)
3005             );
3006             if(opt.prompt){
3007                 activeTextEl.setWidth(w);
3008             }
3009             if(dlg.isVisible()){
3010                 dlg.fixedcenter = false;
3011             }
3012             // to big, make it scroll. = But as usual stupid IE does not support
3013             // !important..
3014             
3015             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3016                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3017                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3018             } else {
3019                 bodyEl.dom.style.height = '';
3020                 bodyEl.dom.style.overflowY = '';
3021             }
3022             if (cw > w) {
3023                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3024             } else {
3025                 bodyEl.dom.style.overflowX = '';
3026             }
3027             
3028             dlg.setContentSize(w, bodyEl.getHeight());
3029             if(dlg.isVisible()){
3030                 dlg.fixedcenter = true;
3031             }
3032             return this;
3033         },
3034
3035         /**
3036          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3037          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3038          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3039          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3040          * @return {Roo.MessageBox} This message box
3041          */
3042         updateProgress : function(value, text){
3043             if(text){
3044                 this.updateText(text);
3045             }
3046             if (pp) { // weird bug on my firefox - for some reason this is not defined
3047                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3048             }
3049             return this;
3050         },        
3051
3052         /**
3053          * Returns true if the message box is currently displayed
3054          * @return {Boolean} True if the message box is visible, else false
3055          */
3056         isVisible : function(){
3057             return dlg && dlg.isVisible();  
3058         },
3059
3060         /**
3061          * Hides the message box if it is displayed
3062          */
3063         hide : function(){
3064             if(this.isVisible()){
3065                 dlg.hide();
3066             }  
3067         },
3068
3069         /**
3070          * Displays a new message box, or reinitializes an existing message box, based on the config options
3071          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3072          * The following config object properties are supported:
3073          * <pre>
3074 Property    Type             Description
3075 ----------  ---------------  ------------------------------------------------------------------------------------
3076 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3077                                    closes (defaults to undefined)
3078 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3079                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3080 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3081                                    progress and wait dialogs will ignore this property and always hide the
3082                                    close button as they can only be closed programmatically.
3083 cls               String           A custom CSS class to apply to the message box element
3084 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3085                                    displayed (defaults to 75)
3086 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3087                                    function will be btn (the name of the button that was clicked, if applicable,
3088                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3089                                    Progress and wait dialogs will ignore this option since they do not respond to
3090                                    user actions and can only be closed programmatically, so any required function
3091                                    should be called by the same code after it closes the dialog.
3092 icon              String           A CSS class that provides a background image to be used as an icon for
3093                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3094 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3095 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3096 modal             Boolean          False to allow user interaction with the page while the message box is
3097                                    displayed (defaults to true)
3098 msg               String           A string that will replace the existing message box body text (defaults
3099                                    to the XHTML-compliant non-breaking space character '&#160;')
3100 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3101 progress          Boolean          True to display a progress bar (defaults to false)
3102 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3103 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3104 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3105 title             String           The title text
3106 value             String           The string value to set into the active textbox element if displayed
3107 wait              Boolean          True to display a progress bar (defaults to false)
3108 width             Number           The width of the dialog in pixels
3109 </pre>
3110          *
3111          * Example usage:
3112          * <pre><code>
3113 Roo.Msg.show({
3114    title: 'Address',
3115    msg: 'Please enter your address:',
3116    width: 300,
3117    buttons: Roo.MessageBox.OKCANCEL,
3118    multiline: true,
3119    fn: saveAddress,
3120    animEl: 'addAddressBtn'
3121 });
3122 </code></pre>
3123          * @param {Object} config Configuration options
3124          * @return {Roo.MessageBox} This message box
3125          */
3126         show : function(options)
3127         {
3128             
3129             // this causes nightmares if you show one dialog after another
3130             // especially on callbacks..
3131              
3132             if(this.isVisible()){
3133                 
3134                 this.hide();
3135                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3136                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3137                 Roo.log("New Dialog Message:" +  options.msg )
3138                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3139                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3140                 
3141             }
3142             var d = this.getDialog();
3143             opt = options;
3144             d.setTitle(opt.title || "&#160;");
3145             d.closeEl.setDisplayed(opt.closable !== false);
3146             activeTextEl = textboxEl;
3147             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3148             if(opt.prompt){
3149                 if(opt.multiline){
3150                     textboxEl.hide();
3151                     textareaEl.show();
3152                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3153                         opt.multiline : this.defaultTextHeight);
3154                     activeTextEl = textareaEl;
3155                 }else{
3156                     textboxEl.show();
3157                     textareaEl.hide();
3158                 }
3159             }else{
3160                 textboxEl.hide();
3161                 textareaEl.hide();
3162             }
3163             progressEl.setDisplayed(opt.progress === true);
3164             this.updateProgress(0);
3165             activeTextEl.dom.value = opt.value || "";
3166             if(opt.prompt){
3167                 dlg.setDefaultButton(activeTextEl);
3168             }else{
3169                 var bs = opt.buttons;
3170                 var db = null;
3171                 if(bs && bs.ok){
3172                     db = buttons["ok"];
3173                 }else if(bs && bs.yes){
3174                     db = buttons["yes"];
3175                 }
3176                 dlg.setDefaultButton(db);
3177             }
3178             bwidth = updateButtons(opt.buttons);
3179             this.updateText(opt.msg);
3180             if(opt.cls){
3181                 d.el.addClass(opt.cls);
3182             }
3183             d.proxyDrag = opt.proxyDrag === true;
3184             d.modal = opt.modal !== false;
3185             d.mask = opt.modal !== false ? mask : false;
3186             if(!d.isVisible()){
3187                 // force it to the end of the z-index stack so it gets a cursor in FF
3188                 document.body.appendChild(dlg.el.dom);
3189                 d.animateTarget = null;
3190                 d.show(options.animEl);
3191             }
3192             return this;
3193         },
3194
3195         /**
3196          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3197          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3198          * and closing the message box when the process is complete.
3199          * @param {String} title The title bar text
3200          * @param {String} msg The message box body text
3201          * @return {Roo.MessageBox} This message box
3202          */
3203         progress : function(title, msg){
3204             this.show({
3205                 title : title,
3206                 msg : msg,
3207                 buttons: false,
3208                 progress:true,
3209                 closable:false,
3210                 minWidth: this.minProgressWidth,
3211                 modal : true
3212             });
3213             return this;
3214         },
3215
3216         /**
3217          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3218          * If a callback function is passed it will be called after the user clicks the button, and the
3219          * id of the button that was clicked will be passed as the only parameter to the callback
3220          * (could also be the top-right close button).
3221          * @param {String} title The title bar text
3222          * @param {String} msg The message box body text
3223          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3224          * @param {Object} scope (optional) The scope of the callback function
3225          * @return {Roo.MessageBox} This message box
3226          */
3227         alert : function(title, msg, fn, scope){
3228             this.show({
3229                 title : title,
3230                 msg : msg,
3231                 buttons: this.OK,
3232                 fn: fn,
3233                 scope : scope,
3234                 modal : true
3235             });
3236             return this;
3237         },
3238
3239         /**
3240          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3241          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3242          * You are responsible for closing the message box when the process is complete.
3243          * @param {String} msg The message box body text
3244          * @param {String} title (optional) The title bar text
3245          * @return {Roo.MessageBox} This message box
3246          */
3247         wait : function(msg, title){
3248             this.show({
3249                 title : title,
3250                 msg : msg,
3251                 buttons: false,
3252                 closable:false,
3253                 progress:true,
3254                 modal:true,
3255                 width:300,
3256                 wait:true
3257             });
3258             waitTimer = Roo.TaskMgr.start({
3259                 run: function(i){
3260                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3261                 },
3262                 interval: 1000
3263             });
3264             return this;
3265         },
3266
3267         /**
3268          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3269          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3270          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3271          * @param {String} title The title bar text
3272          * @param {String} msg The message box body text
3273          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3274          * @param {Object} scope (optional) The scope of the callback function
3275          * @return {Roo.MessageBox} This message box
3276          */
3277         confirm : function(title, msg, fn, scope){
3278             this.show({
3279                 title : title,
3280                 msg : msg,
3281                 buttons: this.YESNO,
3282                 fn: fn,
3283                 scope : scope,
3284                 modal : true
3285             });
3286             return this;
3287         },
3288
3289         /**
3290          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3291          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3292          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3293          * (could also be the top-right close button) and the text that was entered will be passed as the two
3294          * parameters to the callback.
3295          * @param {String} title The title bar text
3296          * @param {String} msg The message box body text
3297          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3298          * @param {Object} scope (optional) The scope of the callback function
3299          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3300          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3301          * @return {Roo.MessageBox} This message box
3302          */
3303         prompt : function(title, msg, fn, scope, multiline){
3304             this.show({
3305                 title : title,
3306                 msg : msg,
3307                 buttons: this.OKCANCEL,
3308                 fn: fn,
3309                 minWidth:250,
3310                 scope : scope,
3311                 prompt:true,
3312                 multiline: multiline,
3313                 modal : true
3314             });
3315             return this;
3316         },
3317
3318         /**
3319          * Button config that displays a single OK button
3320          * @type Object
3321          */
3322         OK : {ok:true},
3323         /**
3324          * Button config that displays Yes and No buttons
3325          * @type Object
3326          */
3327         YESNO : {yes:true, no:true},
3328         /**
3329          * Button config that displays OK and Cancel buttons
3330          * @type Object
3331          */
3332         OKCANCEL : {ok:true, cancel:true},
3333         /**
3334          * Button config that displays Yes, No and Cancel buttons
3335          * @type Object
3336          */
3337         YESNOCANCEL : {yes:true, no:true, cancel:true},
3338
3339         /**
3340          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3341          * @type Number
3342          */
3343         defaultTextHeight : 75,
3344         /**
3345          * The maximum width in pixels of the message box (defaults to 600)
3346          * @type Number
3347          */
3348         maxWidth : 600,
3349         /**
3350          * The minimum width in pixels of the message box (defaults to 100)
3351          * @type Number
3352          */
3353         minWidth : 100,
3354         /**
3355          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3356          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3357          * @type Number
3358          */
3359         minProgressWidth : 250,
3360         /**
3361          * An object containing the default button text strings that can be overriden for localized language support.
3362          * Supported properties are: ok, cancel, yes and no.
3363          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3364          * @type Object
3365          */
3366         buttonText : {
3367             ok : "OK",
3368             cancel : "Cancel",
3369             yes : "Yes",
3370             no : "No"
3371         }
3372     };
3373 }();
3374
3375 /**
3376  * Shorthand for {@link Roo.MessageBox}
3377  */
3378 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3379 Roo.Msg = Roo.Msg || Roo.MessageBox;
3380 /*
3381  * - LGPL
3382  *
3383  * navbar
3384  * 
3385  */
3386
3387 /**
3388  * @class Roo.bootstrap.Navbar
3389  * @extends Roo.bootstrap.Component
3390  * Bootstrap Navbar class
3391
3392  * @constructor
3393  * Create a new Navbar
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Navbar = function(config){
3399     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3400     
3401 };
3402
3403 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3404     
3405     
3406    
3407     // private
3408     navItems : false,
3409     loadMask : false,
3410     
3411     
3412     getAutoCreate : function(){
3413         
3414         
3415         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3416         
3417     },
3418     
3419     initEvents :function ()
3420     {
3421         //Roo.log(this.el.select('.navbar-toggle',true));
3422         this.el.select('.navbar-toggle',true).on('click', function() {
3423            // Roo.log('click');
3424             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3425         }, this);
3426         
3427         var mark = {
3428             tag: "div",
3429             cls:"x-dlg-mask"
3430         };
3431         
3432         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3433         
3434         var size = this.el.getSize();
3435         this.maskEl.setSize(size.width, size.height);
3436         this.maskEl.enableDisplayMode("block");
3437         this.maskEl.hide();
3438         
3439         if(this.loadMask){
3440             this.maskEl.show();
3441         }
3442     },
3443     
3444     
3445     getChildContainer : function()
3446     {
3447         if (this.el.select('.collapse').getCount()) {
3448             return this.el.select('.collapse',true).first();
3449         }
3450         
3451         return this.el;
3452     },
3453     
3454     mask : function()
3455     {
3456         this.maskEl.show();
3457     },
3458     
3459     unmask : function()
3460     {
3461         this.maskEl.hide();
3462     } 
3463     
3464     
3465     
3466     
3467 });
3468
3469
3470
3471  
3472
3473  /*
3474  * - LGPL
3475  *
3476  * navbar
3477  * 
3478  */
3479
3480 /**
3481  * @class Roo.bootstrap.NavSimplebar
3482  * @extends Roo.bootstrap.Navbar
3483  * Bootstrap Sidebar class
3484  *
3485  * @cfg {Boolean} inverse is inverted color
3486  * 
3487  * @cfg {String} type (nav | pills | tabs)
3488  * @cfg {Boolean} arrangement stacked | justified
3489  * @cfg {String} align (left | right) alignment
3490  * 
3491  * @cfg {Boolean} main (true|false) main nav bar? default false
3492  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3493  * 
3494  * @cfg {String} tag (header|footer|nav|div) default is nav 
3495
3496  * 
3497  * 
3498  * 
3499  * @constructor
3500  * Create a new Sidebar
3501  * @param {Object} config The config object
3502  */
3503
3504
3505 Roo.bootstrap.NavSimplebar = function(config){
3506     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3507 };
3508
3509 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3510     
3511     inverse: false,
3512     
3513     type: false,
3514     arrangement: '',
3515     align : false,
3516     
3517     
3518     
3519     main : false,
3520     
3521     
3522     tag : false,
3523     
3524     
3525     getAutoCreate : function(){
3526         
3527         
3528         var cfg = {
3529             tag : this.tag || 'div',
3530             cls : 'navbar'
3531         };
3532           
3533         
3534         cfg.cn = [
3535             {
3536                 cls: 'nav',
3537                 tag : 'ul'
3538             }
3539         ];
3540         
3541          
3542         this.type = this.type || 'nav';
3543         if (['tabs','pills'].indexOf(this.type)!==-1) {
3544             cfg.cn[0].cls += ' nav-' + this.type
3545         
3546         
3547         } else {
3548             if (this.type!=='nav') {
3549                 Roo.log('nav type must be nav/tabs/pills')
3550             }
3551             cfg.cn[0].cls += ' navbar-nav'
3552         }
3553         
3554         
3555         
3556         
3557         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3558             cfg.cn[0].cls += ' nav-' + this.arrangement;
3559         }
3560         
3561         
3562         if (this.align === 'right') {
3563             cfg.cn[0].cls += ' navbar-right';
3564         }
3565         
3566         if (this.inverse) {
3567             cfg.cls += ' navbar-inverse';
3568             
3569         }
3570         
3571         
3572         return cfg;
3573     
3574         
3575     }
3576     
3577     
3578     
3579 });
3580
3581
3582
3583  
3584
3585  
3586        /*
3587  * - LGPL
3588  *
3589  * navbar
3590  * 
3591  */
3592
3593 /**
3594  * @class Roo.bootstrap.NavHeaderbar
3595  * @extends Roo.bootstrap.NavSimplebar
3596  * Bootstrap Sidebar class
3597  *
3598  * @cfg {String} brand what is brand
3599  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3600  * @cfg {String} brand_href href of the brand
3601  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3602  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3603  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3604  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3605  * 
3606  * @constructor
3607  * Create a new Sidebar
3608  * @param {Object} config The config object
3609  */
3610
3611
3612 Roo.bootstrap.NavHeaderbar = function(config){
3613     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3614       
3615 };
3616
3617 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3618     
3619     position: '',
3620     brand: '',
3621     brand_href: false,
3622     srButton : true,
3623     autohide : false,
3624     desktopCenter : false,
3625    
3626     
3627     getAutoCreate : function(){
3628         
3629         var   cfg = {
3630             tag: this.nav || 'nav',
3631             cls: 'navbar',
3632             role: 'navigation',
3633             cn: []
3634         };
3635         
3636         var cn = cfg.cn;
3637         if (this.desktopCenter) {
3638             cn.push({cls : 'container', cn : []});
3639             cn = cn[0].cn;
3640         }
3641         
3642         if(this.srButton){
3643             cn.push({
3644                 tag: 'div',
3645                 cls: 'navbar-header',
3646                 cn: [
3647                     {
3648                         tag: 'button',
3649                         type: 'button',
3650                         cls: 'navbar-toggle',
3651                         'data-toggle': 'collapse',
3652                         cn: [
3653                             {
3654                                 tag: 'span',
3655                                 cls: 'sr-only',
3656                                 html: 'Toggle navigation'
3657                             },
3658                             {
3659                                 tag: 'span',
3660                                 cls: 'icon-bar'
3661                             },
3662                             {
3663                                 tag: 'span',
3664                                 cls: 'icon-bar'
3665                             },
3666                             {
3667                                 tag: 'span',
3668                                 cls: 'icon-bar'
3669                             }
3670                         ]
3671                     }
3672                 ]
3673             });
3674         }
3675         
3676         cn.push({
3677             tag: 'div',
3678             cls: 'collapse navbar-collapse',
3679             cn : []
3680         });
3681         
3682         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3683         
3684         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3685             cfg.cls += ' navbar-' + this.position;
3686             
3687             // tag can override this..
3688             
3689             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3690         }
3691         
3692         if (this.brand !== '') {
3693             cn[0].cn.push({
3694                 tag: 'a',
3695                 href: this.brand_href ? this.brand_href : '#',
3696                 cls: 'navbar-brand',
3697                 cn: [
3698                 this.brand
3699                 ]
3700             });
3701         }
3702         
3703         if(this.main){
3704             cfg.cls += ' main-nav';
3705         }
3706         
3707         
3708         return cfg;
3709
3710         
3711     },
3712     getHeaderChildContainer : function()
3713     {
3714         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3715             return this.el.select('.navbar-header',true).first();
3716         }
3717         
3718         return this.getChildContainer();
3719     },
3720     
3721     
3722     initEvents : function()
3723     {
3724         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3725         
3726         if (this.autohide) {
3727             
3728             var prevScroll = 0;
3729             var ft = this.el;
3730             
3731             Roo.get(document).on('scroll',function(e) {
3732                 var ns = Roo.get(document).getScroll().top;
3733                 var os = prevScroll;
3734                 prevScroll = ns;
3735                 
3736                 if(ns > os){
3737                     ft.removeClass('slideDown');
3738                     ft.addClass('slideUp');
3739                     return;
3740                 }
3741                 ft.removeClass('slideUp');
3742                 ft.addClass('slideDown');
3743                  
3744               
3745           },this);
3746         }
3747     }    
3748     
3749 });
3750
3751
3752
3753  
3754
3755  /*
3756  * - LGPL
3757  *
3758  * navbar
3759  * 
3760  */
3761
3762 /**
3763  * @class Roo.bootstrap.NavSidebar
3764  * @extends Roo.bootstrap.Navbar
3765  * Bootstrap Sidebar class
3766  * 
3767  * @constructor
3768  * Create a new Sidebar
3769  * @param {Object} config The config object
3770  */
3771
3772
3773 Roo.bootstrap.NavSidebar = function(config){
3774     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3775 };
3776
3777 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3778     
3779     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3780     
3781     getAutoCreate : function(){
3782         
3783         
3784         return  {
3785             tag: 'div',
3786             cls: 'sidebar sidebar-nav'
3787         };
3788     
3789         
3790     }
3791     
3792     
3793     
3794 });
3795
3796
3797
3798  
3799
3800  /*
3801  * - LGPL
3802  *
3803  * nav group
3804  * 
3805  */
3806
3807 /**
3808  * @class Roo.bootstrap.NavGroup
3809  * @extends Roo.bootstrap.Component
3810  * Bootstrap NavGroup class
3811  * @cfg {String} align (left|right)
3812  * @cfg {Boolean} inverse
3813  * @cfg {String} type (nav|pills|tab) default nav
3814  * @cfg {String} navId - reference Id for navbar.
3815
3816  * 
3817  * @constructor
3818  * Create a new nav group
3819  * @param {Object} config The config object
3820  */
3821
3822 Roo.bootstrap.NavGroup = function(config){
3823     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3824     this.navItems = [];
3825    
3826     Roo.bootstrap.NavGroup.register(this);
3827      this.addEvents({
3828         /**
3829              * @event changed
3830              * Fires when the active item changes
3831              * @param {Roo.bootstrap.NavGroup} this
3832              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3833              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3834          */
3835         'changed': true
3836      });
3837     
3838 };
3839
3840 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3841     
3842     align: '',
3843     inverse: false,
3844     form: false,
3845     type: 'nav',
3846     navId : '',
3847     // private
3848     
3849     navItems : false, 
3850     
3851     getAutoCreate : function()
3852     {
3853         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3854         
3855         cfg = {
3856             tag : 'ul',
3857             cls: 'nav' 
3858         };
3859         
3860         if (['tabs','pills'].indexOf(this.type)!==-1) {
3861             cfg.cls += ' nav-' + this.type
3862         } else {
3863             if (this.type!=='nav') {
3864                 Roo.log('nav type must be nav/tabs/pills')
3865             }
3866             cfg.cls += ' navbar-nav'
3867         }
3868         
3869         if (this.parent().sidebar) {
3870             cfg = {
3871                 tag: 'ul',
3872                 cls: 'dashboard-menu sidebar-menu'
3873             };
3874             
3875             return cfg;
3876         }
3877         
3878         if (this.form === true) {
3879             cfg = {
3880                 tag: 'form',
3881                 cls: 'navbar-form'
3882             };
3883             
3884             if (this.align === 'right') {
3885                 cfg.cls += ' navbar-right';
3886             } else {
3887                 cfg.cls += ' navbar-left';
3888             }
3889         }
3890         
3891         if (this.align === 'right') {
3892             cfg.cls += ' navbar-right';
3893         }
3894         
3895         if (this.inverse) {
3896             cfg.cls += ' navbar-inverse';
3897             
3898         }
3899         
3900         
3901         return cfg;
3902     },
3903     /**
3904     * sets the active Navigation item
3905     * @param {Roo.bootstrap.NavItem} the new current navitem
3906     */
3907     setActiveItem : function(item)
3908     {
3909         var prev = false;
3910         Roo.each(this.navItems, function(v){
3911             if (v == item) {
3912                 return ;
3913             }
3914             if (v.isActive()) {
3915                 v.setActive(false, true);
3916                 prev = v;
3917                 
3918             }
3919             
3920         });
3921
3922         item.setActive(true, true);
3923         this.fireEvent('changed', this, item, prev);
3924         
3925         
3926     },
3927     /**
3928     * gets the active Navigation item
3929     * @return {Roo.bootstrap.NavItem} the current navitem
3930     */
3931     getActive : function()
3932     {
3933         
3934         var prev = false;
3935         Roo.each(this.navItems, function(v){
3936             
3937             if (v.isActive()) {
3938                 prev = v;
3939                 
3940             }
3941             
3942         });
3943         return prev;
3944     },
3945     
3946     indexOfNav : function()
3947     {
3948         
3949         var prev = false;
3950         Roo.each(this.navItems, function(v,i){
3951             
3952             if (v.isActive()) {
3953                 prev = i;
3954                 
3955             }
3956             
3957         });
3958         return prev;
3959     },
3960     /**
3961     * adds a Navigation item
3962     * @param {Roo.bootstrap.NavItem} the navitem to add
3963     */
3964     addItem : function(cfg)
3965     {
3966         var cn = new Roo.bootstrap.NavItem(cfg);
3967         this.register(cn);
3968         cn.parentId = this.id;
3969         cn.onRender(this.el, null);
3970         return cn;
3971     },
3972     /**
3973     * register a Navigation item
3974     * @param {Roo.bootstrap.NavItem} the navitem to add
3975     */
3976     register : function(item)
3977     {
3978         this.navItems.push( item);
3979         item.navId = this.navId;
3980     
3981     },
3982     
3983     /**
3984     * clear all the Navigation item
3985     */
3986    
3987     clearAll : function()
3988     {
3989         this.navItems = [];
3990         this.el.dom.innerHTML = '';
3991     },
3992     
3993     getNavItem: function(tabId)
3994     {
3995         var ret = false;
3996         Roo.each(this.navItems, function(e) {
3997             if (e.tabId == tabId) {
3998                ret =  e;
3999                return false;
4000             }
4001             return true;
4002             
4003         });
4004         return ret;
4005     },
4006     
4007     setActiveNext : function()
4008     {
4009         var i = this.indexOfNav(this.getActive());
4010         if (i > this.navItems.length) {
4011             return;
4012         }
4013         this.setActiveItem(this.navItems[i+1]);
4014     },
4015     setActivePrev : function()
4016     {
4017         var i = this.indexOfNav(this.getActive());
4018         if (i  < 1) {
4019             return;
4020         }
4021         this.setActiveItem(this.navItems[i-1]);
4022     },
4023     clearWasActive : function(except) {
4024         Roo.each(this.navItems, function(e) {
4025             if (e.tabId != except.tabId && e.was_active) {
4026                e.was_active = false;
4027                return false;
4028             }
4029             return true;
4030             
4031         });
4032     },
4033     getWasActive : function ()
4034     {
4035         var r = false;
4036         Roo.each(this.navItems, function(e) {
4037             if (e.was_active) {
4038                r = e;
4039                return false;
4040             }
4041             return true;
4042             
4043         });
4044         return r;
4045     }
4046     
4047     
4048 });
4049
4050  
4051 Roo.apply(Roo.bootstrap.NavGroup, {
4052     
4053     groups: {},
4054      /**
4055     * register a Navigation Group
4056     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4057     */
4058     register : function(navgrp)
4059     {
4060         this.groups[navgrp.navId] = navgrp;
4061         
4062     },
4063     /**
4064     * fetch a Navigation Group based on the navigation ID
4065     * @param {string} the navgroup to add
4066     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4067     */
4068     get: function(navId) {
4069         if (typeof(this.groups[navId]) == 'undefined') {
4070             return false;
4071             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4072         }
4073         return this.groups[navId] ;
4074     }
4075     
4076     
4077     
4078 });
4079
4080  /*
4081  * - LGPL
4082  *
4083  * row
4084  * 
4085  */
4086
4087 /**
4088  * @class Roo.bootstrap.NavItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap Navbar.NavItem class
4091  * @cfg {String} href  link to
4092  * @cfg {String} html content of button
4093  * @cfg {String} badge text inside badge
4094  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4095  * @cfg {String} glyphicon name of glyphicon
4096  * @cfg {String} icon name of font awesome icon
4097  * @cfg {Boolean} active Is item active
4098  * @cfg {Boolean} disabled Is item disabled
4099  
4100  * @cfg {Boolean} preventDefault (true | false) default false
4101  * @cfg {String} tabId the tab that this item activates.
4102  * @cfg {String} tagtype (a|span) render as a href or span?
4103  * @cfg {Boolean} animateRef (true|false) link to element default false  
4104   
4105  * @constructor
4106  * Create a new Navbar Item
4107  * @param {Object} config The config object
4108  */
4109 Roo.bootstrap.NavItem = function(config){
4110     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4111     this.addEvents({
4112         // raw events
4113         /**
4114          * @event click
4115          * The raw click event for the entire grid.
4116          * @param {Roo.EventObject} e
4117          */
4118         "click" : true,
4119          /**
4120             * @event changed
4121             * Fires when the active item active state changes
4122             * @param {Roo.bootstrap.NavItem} this
4123             * @param {boolean} state the new state
4124              
4125          */
4126         'changed': true,
4127         /**
4128             * @event scrollto
4129             * Fires when scroll to element
4130             * @param {Roo.bootstrap.NavItem} this
4131             * @param {Object} options
4132             * @param {Roo.EventObject} e
4133              
4134          */
4135         'scrollto': true
4136     });
4137    
4138 };
4139
4140 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4141     
4142     href: false,
4143     html: '',
4144     badge: '',
4145     icon: false,
4146     glyphicon: false,
4147     active: false,
4148     preventDefault : false,
4149     tabId : false,
4150     tagtype : 'a',
4151     disabled : false,
4152     animateRef : false,
4153     was_active : false,
4154     
4155     getAutoCreate : function(){
4156          
4157         var cfg = {
4158             tag: 'li',
4159             cls: 'nav-item'
4160             
4161         };
4162         
4163         if (this.active) {
4164             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4165         }
4166         if (this.disabled) {
4167             cfg.cls += ' disabled';
4168         }
4169         
4170         if (this.href || this.html || this.glyphicon || this.icon) {
4171             cfg.cn = [
4172                 {
4173                     tag: this.tagtype,
4174                     href : this.href || "#",
4175                     html: this.html || ''
4176                 }
4177             ];
4178             
4179             if (this.icon) {
4180                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4181             }
4182
4183             if(this.glyphicon) {
4184                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4185             }
4186             
4187             if (this.menu) {
4188                 
4189                 cfg.cn[0].html += " <span class='caret'></span>";
4190              
4191             }
4192             
4193             if (this.badge !== '') {
4194                  
4195                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4196             }
4197         }
4198         
4199         
4200         
4201         return cfg;
4202     },
4203     initEvents: function() 
4204     {
4205         if (typeof (this.menu) != 'undefined') {
4206             this.menu.parentType = this.xtype;
4207             this.menu.triggerEl = this.el;
4208             this.menu = this.addxtype(Roo.apply({}, this.menu));
4209         }
4210         
4211         this.el.select('a',true).on('click', this.onClick, this);
4212         
4213         if(this.tagtype == 'span'){
4214             this.el.select('span',true).on('click', this.onClick, this);
4215         }
4216        
4217         // at this point parent should be available..
4218         this.parent().register(this);
4219     },
4220     
4221     onClick : function(e)
4222     {
4223         if (e.getTarget('.dropdown-menu-item')) {
4224             // did you click on a menu itemm.... - then don't trigger onclick..
4225             return;
4226         }
4227         
4228         if(
4229                 this.preventDefault || 
4230                 this.href == '#' 
4231         ){
4232             Roo.log("NavItem - prevent Default?");
4233             e.preventDefault();
4234         }
4235         
4236         if (this.disabled) {
4237             return;
4238         }
4239         
4240         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4241         if (tg && tg.transition) {
4242             Roo.log("waiting for the transitionend");
4243             return;
4244         }
4245         
4246         
4247         
4248         //Roo.log("fire event clicked");
4249         if(this.fireEvent('click', this, e) === false){
4250             return;
4251         };
4252         
4253         if(this.tagtype == 'span'){
4254             return;
4255         }
4256         
4257         //Roo.log(this.href);
4258         var ael = this.el.select('a',true).first();
4259         //Roo.log(ael);
4260         
4261         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4262             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4263             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4264                 return; // ignore... - it's a 'hash' to another page.
4265             }
4266             Roo.log("NavItem - prevent Default?");
4267             e.preventDefault();
4268             this.scrollToElement(e);
4269         }
4270         
4271         
4272         var p =  this.parent();
4273    
4274         if (['tabs','pills'].indexOf(p.type)!==-1) {
4275             if (typeof(p.setActiveItem) !== 'undefined') {
4276                 p.setActiveItem(this);
4277             }
4278         }
4279         
4280         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4281         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4282             // remove the collapsed menu expand...
4283             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4284         }
4285     },
4286     
4287     isActive: function () {
4288         return this.active
4289     },
4290     setActive : function(state, fire, is_was_active)
4291     {
4292         if (this.active && !state && this.navId) {
4293             this.was_active = true;
4294             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4295             if (nv) {
4296                 nv.clearWasActive(this);
4297             }
4298             
4299         }
4300         this.active = state;
4301         
4302         if (!state ) {
4303             this.el.removeClass('active');
4304         } else if (!this.el.hasClass('active')) {
4305             this.el.addClass('active');
4306         }
4307         if (fire) {
4308             this.fireEvent('changed', this, state);
4309         }
4310         
4311         // show a panel if it's registered and related..
4312         
4313         if (!this.navId || !this.tabId || !state || is_was_active) {
4314             return;
4315         }
4316         
4317         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4318         if (!tg) {
4319             return;
4320         }
4321         var pan = tg.getPanelByName(this.tabId);
4322         if (!pan) {
4323             return;
4324         }
4325         // if we can not flip to new panel - go back to old nav highlight..
4326         if (false == tg.showPanel(pan)) {
4327             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4328             if (nv) {
4329                 var onav = nv.getWasActive();
4330                 if (onav) {
4331                     onav.setActive(true, false, true);
4332                 }
4333             }
4334             
4335         }
4336         
4337         
4338         
4339     },
4340      // this should not be here...
4341     setDisabled : function(state)
4342     {
4343         this.disabled = state;
4344         if (!state ) {
4345             this.el.removeClass('disabled');
4346         } else if (!this.el.hasClass('disabled')) {
4347             this.el.addClass('disabled');
4348         }
4349         
4350     },
4351     
4352     /**
4353      * Fetch the element to display the tooltip on.
4354      * @return {Roo.Element} defaults to this.el
4355      */
4356     tooltipEl : function()
4357     {
4358         return this.el.select('' + this.tagtype + '', true).first();
4359     },
4360     
4361     scrollToElement : function(e)
4362     {
4363         var c = document.body;
4364         
4365         /*
4366          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4367          */
4368         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4369             c = document.documentElement;
4370         }
4371         
4372         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4373         
4374         if(!target){
4375             return;
4376         }
4377
4378         var o = target.calcOffsetsTo(c);
4379         
4380         var options = {
4381             target : target,
4382             value : o[1]
4383         };
4384         
4385         this.fireEvent('scrollto', this, options, e);
4386         
4387         Roo.get(c).scrollTo('top', options.value, true);
4388         
4389         return;
4390     }
4391 });
4392  
4393
4394  /*
4395  * - LGPL
4396  *
4397  * sidebar item
4398  *
4399  *  li
4400  *    <span> icon </span>
4401  *    <span> text </span>
4402  *    <span>badge </span>
4403  */
4404
4405 /**
4406  * @class Roo.bootstrap.NavSidebarItem
4407  * @extends Roo.bootstrap.NavItem
4408  * Bootstrap Navbar.NavSidebarItem class
4409  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4410  * {bool} open is the menu open
4411  * @constructor
4412  * Create a new Navbar Button
4413  * @param {Object} config The config object
4414  */
4415 Roo.bootstrap.NavSidebarItem = function(config){
4416     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4417     this.addEvents({
4418         // raw events
4419         /**
4420          * @event click
4421          * The raw click event for the entire grid.
4422          * @param {Roo.EventObject} e
4423          */
4424         "click" : true,
4425          /**
4426             * @event changed
4427             * Fires when the active item active state changes
4428             * @param {Roo.bootstrap.NavSidebarItem} this
4429             * @param {boolean} state the new state
4430              
4431          */
4432         'changed': true
4433     });
4434    
4435 };
4436
4437 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4438     
4439     badgeWeight : 'default',
4440     
4441     open: false,
4442     
4443     getAutoCreate : function(){
4444         
4445         
4446         var a = {
4447                 tag: 'a',
4448                 href : this.href || '#',
4449                 cls: '',
4450                 html : '',
4451                 cn : []
4452         };
4453         var cfg = {
4454             tag: 'li',
4455             cls: '',
4456             cn: [ a ]
4457         };
4458         var span = {
4459             tag: 'span',
4460             html : this.html || ''
4461         };
4462         
4463         
4464         if (this.active) {
4465             cfg.cls += ' active';
4466         }
4467         
4468         if (this.disabled) {
4469             cfg.cls += ' disabled';
4470         }
4471         if (this.open) {
4472             cfg.cls += ' open x-open';
4473         }
4474         // left icon..
4475         if (this.glyphicon || this.icon) {
4476             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4477             a.cn.push({ tag : 'i', cls : c }) ;
4478         }
4479         // html..
4480         a.cn.push(span);
4481         // then badge..
4482         if (this.badge !== '') {
4483             
4484             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4485         }
4486         // fi
4487         if (this.menu) {
4488             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4489             a.cls += 'dropdown-toggle treeview' ;
4490             
4491         }
4492         
4493         
4494         
4495         return cfg;
4496          
4497            
4498     },
4499     
4500     initEvents : function()
4501     { 
4502         if (typeof (this.menu) != 'undefined') {
4503             this.menu.parentType = this.xtype;
4504             this.menu.triggerEl = this.el;
4505             this.menu = this.addxtype(Roo.apply({}, this.menu));
4506         }
4507         
4508         this.el.on('click', this.onClick, this);
4509        
4510     
4511         if(this.badge !== ''){
4512  
4513             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4514         }
4515         
4516     },
4517     
4518     onClick : function(e)
4519     {
4520         if(this.disabled){
4521             e.preventDefault();
4522             return;
4523         }
4524         
4525         if(this.preventDefault){
4526             e.preventDefault();
4527         }
4528         
4529         this.fireEvent('click', this);
4530     },
4531     
4532     disable : function()
4533     {
4534         this.setDisabled(true);
4535     },
4536     
4537     enable : function()
4538     {
4539         this.setDisabled(false);
4540     },
4541     
4542     setDisabled : function(state)
4543     {
4544         if(this.disabled == state){
4545             return;
4546         }
4547         
4548         this.disabled = state;
4549         
4550         if (state) {
4551             this.el.addClass('disabled');
4552             return;
4553         }
4554         
4555         this.el.removeClass('disabled');
4556         
4557         return;
4558     },
4559     
4560     setActive : function(state)
4561     {
4562         if(this.active == state){
4563             return;
4564         }
4565         
4566         this.active = state;
4567         
4568         if (state) {
4569             this.el.addClass('active');
4570             return;
4571         }
4572         
4573         this.el.removeClass('active');
4574         
4575         return;
4576     },
4577     
4578     isActive: function () 
4579     {
4580         return this.active;
4581     },
4582     
4583     setBadge : function(str)
4584     {
4585         if(!this.badgeEl){
4586             return;
4587         }
4588         
4589         this.badgeEl.dom.innerHTML = str;
4590     }
4591     
4592    
4593      
4594  
4595 });
4596  
4597
4598  /*
4599  * - LGPL
4600  *
4601  * row
4602  * 
4603  */
4604
4605 /**
4606  * @class Roo.bootstrap.Row
4607  * @extends Roo.bootstrap.Component
4608  * Bootstrap Row class (contains columns...)
4609  * 
4610  * @constructor
4611  * Create a new Row
4612  * @param {Object} config The config object
4613  */
4614
4615 Roo.bootstrap.Row = function(config){
4616     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4617 };
4618
4619 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4620     
4621     getAutoCreate : function(){
4622        return {
4623             cls: 'row clearfix'
4624        };
4625     }
4626     
4627     
4628 });
4629
4630  
4631
4632  /*
4633  * - LGPL
4634  *
4635  * element
4636  * 
4637  */
4638
4639 /**
4640  * @class Roo.bootstrap.Element
4641  * @extends Roo.bootstrap.Component
4642  * Bootstrap Element class
4643  * @cfg {String} html contents of the element
4644  * @cfg {String} tag tag of the element
4645  * @cfg {String} cls class of the element
4646  * @cfg {Boolean} preventDefault (true|false) default false
4647  * @cfg {Boolean} clickable (true|false) default false
4648  * 
4649  * @constructor
4650  * Create a new Element
4651  * @param {Object} config The config object
4652  */
4653
4654 Roo.bootstrap.Element = function(config){
4655     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4656     
4657     this.addEvents({
4658         // raw events
4659         /**
4660          * @event click
4661          * When a element is chick
4662          * @param {Roo.bootstrap.Element} this
4663          * @param {Roo.EventObject} e
4664          */
4665         "click" : true
4666     });
4667 };
4668
4669 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4670     
4671     tag: 'div',
4672     cls: '',
4673     html: '',
4674     preventDefault: false, 
4675     clickable: false,
4676     
4677     getAutoCreate : function(){
4678         
4679         var cfg = {
4680             tag: this.tag,
4681             cls: this.cls,
4682             html: this.html
4683         };
4684         
4685         return cfg;
4686     },
4687     
4688     initEvents: function() 
4689     {
4690         Roo.bootstrap.Element.superclass.initEvents.call(this);
4691         
4692         if(this.clickable){
4693             this.el.on('click', this.onClick, this);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.preventDefault){
4701             e.preventDefault();
4702         }
4703         
4704         this.fireEvent('click', this, e);
4705     },
4706     
4707     getValue : function()
4708     {
4709         return this.el.dom.innerHTML;
4710     },
4711     
4712     setValue : function(value)
4713     {
4714         this.el.dom.innerHTML = value;
4715     }
4716    
4717 });
4718
4719  
4720
4721  /*
4722  * - LGPL
4723  *
4724  * pagination
4725  * 
4726  */
4727
4728 /**
4729  * @class Roo.bootstrap.Pagination
4730  * @extends Roo.bootstrap.Component
4731  * Bootstrap Pagination class
4732  * @cfg {String} size xs | sm | md | lg
4733  * @cfg {Boolean} inverse false | true
4734  * 
4735  * @constructor
4736  * Create a new Pagination
4737  * @param {Object} config The config object
4738  */
4739
4740 Roo.bootstrap.Pagination = function(config){
4741     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4742 };
4743
4744 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4745     
4746     cls: false,
4747     size: false,
4748     inverse: false,
4749     
4750     getAutoCreate : function(){
4751         var cfg = {
4752             tag: 'ul',
4753                 cls: 'pagination'
4754         };
4755         if (this.inverse) {
4756             cfg.cls += ' inverse';
4757         }
4758         if (this.html) {
4759             cfg.html=this.html;
4760         }
4761         if (this.cls) {
4762             cfg.cls += " " + this.cls;
4763         }
4764         return cfg;
4765     }
4766    
4767 });
4768
4769  
4770
4771  /*
4772  * - LGPL
4773  *
4774  * Pagination item
4775  * 
4776  */
4777
4778
4779 /**
4780  * @class Roo.bootstrap.PaginationItem
4781  * @extends Roo.bootstrap.Component
4782  * Bootstrap PaginationItem class
4783  * @cfg {String} html text
4784  * @cfg {String} href the link
4785  * @cfg {Boolean} preventDefault (true | false) default true
4786  * @cfg {Boolean} active (true | false) default false
4787  * @cfg {Boolean} disabled default false
4788  * 
4789  * 
4790  * @constructor
4791  * Create a new PaginationItem
4792  * @param {Object} config The config object
4793  */
4794
4795
4796 Roo.bootstrap.PaginationItem = function(config){
4797     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4798     this.addEvents({
4799         // raw events
4800         /**
4801          * @event click
4802          * The raw click event for the entire grid.
4803          * @param {Roo.EventObject} e
4804          */
4805         "click" : true
4806     });
4807 };
4808
4809 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4810     
4811     href : false,
4812     html : false,
4813     preventDefault: true,
4814     active : false,
4815     cls : false,
4816     disabled: false,
4817     
4818     getAutoCreate : function(){
4819         var cfg= {
4820             tag: 'li',
4821             cn: [
4822                 {
4823                     tag : 'a',
4824                     href : this.href ? this.href : '#',
4825                     html : this.html ? this.html : ''
4826                 }
4827             ]
4828         };
4829         
4830         if(this.cls){
4831             cfg.cls = this.cls;
4832         }
4833         
4834         if(this.disabled){
4835             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4836         }
4837         
4838         if(this.active){
4839             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4840         }
4841         
4842         return cfg;
4843     },
4844     
4845     initEvents: function() {
4846         
4847         this.el.on('click', this.onClick, this);
4848         
4849     },
4850     onClick : function(e)
4851     {
4852         Roo.log('PaginationItem on click ');
4853         if(this.preventDefault){
4854             e.preventDefault();
4855         }
4856         
4857         if(this.disabled){
4858             return;
4859         }
4860         
4861         this.fireEvent('click', this, e);
4862     }
4863    
4864 });
4865
4866  
4867
4868  /*
4869  * - LGPL
4870  *
4871  * slider
4872  * 
4873  */
4874
4875
4876 /**
4877  * @class Roo.bootstrap.Slider
4878  * @extends Roo.bootstrap.Component
4879  * Bootstrap Slider class
4880  *    
4881  * @constructor
4882  * Create a new Slider
4883  * @param {Object} config The config object
4884  */
4885
4886 Roo.bootstrap.Slider = function(config){
4887     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4888 };
4889
4890 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4891     
4892     getAutoCreate : function(){
4893         
4894         var cfg = {
4895             tag: 'div',
4896             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4897             cn: [
4898                 {
4899                     tag: 'a',
4900                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4901                 }
4902             ]
4903         };
4904         
4905         return cfg;
4906     }
4907    
4908 });
4909
4910  /*
4911  * Based on:
4912  * Ext JS Library 1.1.1
4913  * Copyright(c) 2006-2007, Ext JS, LLC.
4914  *
4915  * Originally Released Under LGPL - original licence link has changed is not relivant.
4916  *
4917  * Fork - LGPL
4918  * <script type="text/javascript">
4919  */
4920  
4921
4922 /**
4923  * @class Roo.grid.ColumnModel
4924  * @extends Roo.util.Observable
4925  * This is the default implementation of a ColumnModel used by the Grid. It defines
4926  * the columns in the grid.
4927  * <br>Usage:<br>
4928  <pre><code>
4929  var colModel = new Roo.grid.ColumnModel([
4930         {header: "Ticker", width: 60, sortable: true, locked: true},
4931         {header: "Company Name", width: 150, sortable: true},
4932         {header: "Market Cap.", width: 100, sortable: true},
4933         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4934         {header: "Employees", width: 100, sortable: true, resizable: false}
4935  ]);
4936  </code></pre>
4937  * <p>
4938  
4939  * The config options listed for this class are options which may appear in each
4940  * individual column definition.
4941  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4942  * @constructor
4943  * @param {Object} config An Array of column config objects. See this class's
4944  * config objects for details.
4945 */
4946 Roo.grid.ColumnModel = function(config){
4947         /**
4948      * The config passed into the constructor
4949      */
4950     this.config = config;
4951     this.lookup = {};
4952
4953     // if no id, create one
4954     // if the column does not have a dataIndex mapping,
4955     // map it to the order it is in the config
4956     for(var i = 0, len = config.length; i < len; i++){
4957         var c = config[i];
4958         if(typeof c.dataIndex == "undefined"){
4959             c.dataIndex = i;
4960         }
4961         if(typeof c.renderer == "string"){
4962             c.renderer = Roo.util.Format[c.renderer];
4963         }
4964         if(typeof c.id == "undefined"){
4965             c.id = Roo.id();
4966         }
4967         if(c.editor && c.editor.xtype){
4968             c.editor  = Roo.factory(c.editor, Roo.grid);
4969         }
4970         if(c.editor && c.editor.isFormField){
4971             c.editor = new Roo.grid.GridEditor(c.editor);
4972         }
4973         this.lookup[c.id] = c;
4974     }
4975
4976     /**
4977      * The width of columns which have no width specified (defaults to 100)
4978      * @type Number
4979      */
4980     this.defaultWidth = 100;
4981
4982     /**
4983      * Default sortable of columns which have no sortable specified (defaults to false)
4984      * @type Boolean
4985      */
4986     this.defaultSortable = false;
4987
4988     this.addEvents({
4989         /**
4990              * @event widthchange
4991              * Fires when the width of a column changes.
4992              * @param {ColumnModel} this
4993              * @param {Number} columnIndex The column index
4994              * @param {Number} newWidth The new width
4995              */
4996             "widthchange": true,
4997         /**
4998              * @event headerchange
4999              * Fires when the text of a header changes.
5000              * @param {ColumnModel} this
5001              * @param {Number} columnIndex The column index
5002              * @param {Number} newText The new header text
5003              */
5004             "headerchange": true,
5005         /**
5006              * @event hiddenchange
5007              * Fires when a column is hidden or "unhidden".
5008              * @param {ColumnModel} this
5009              * @param {Number} columnIndex The column index
5010              * @param {Boolean} hidden true if hidden, false otherwise
5011              */
5012             "hiddenchange": true,
5013             /**
5014          * @event columnmoved
5015          * Fires when a column is moved.
5016          * @param {ColumnModel} this
5017          * @param {Number} oldIndex
5018          * @param {Number} newIndex
5019          */
5020         "columnmoved" : true,
5021         /**
5022          * @event columlockchange
5023          * Fires when a column's locked state is changed
5024          * @param {ColumnModel} this
5025          * @param {Number} colIndex
5026          * @param {Boolean} locked true if locked
5027          */
5028         "columnlockchange" : true
5029     });
5030     Roo.grid.ColumnModel.superclass.constructor.call(this);
5031 };
5032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5033     /**
5034      * @cfg {String} header The header text to display in the Grid view.
5035      */
5036     /**
5037      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5038      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5039      * specified, the column's index is used as an index into the Record's data Array.
5040      */
5041     /**
5042      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5043      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5044      */
5045     /**
5046      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5047      * Defaults to the value of the {@link #defaultSortable} property.
5048      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5049      */
5050     /**
5051      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5052      */
5053     /**
5054      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5055      */
5056     /**
5057      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5058      */
5059     /**
5060      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5061      */
5062     /**
5063      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5064      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5065      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5066      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5067      */
5068        /**
5069      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5070      */
5071     /**
5072      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5073      */
5074     /**
5075      * @cfg {String} cursor (Optional)
5076      */
5077     /**
5078      * @cfg {String} tooltip (Optional)
5079      */
5080     /**
5081      * @cfg {Number} xs (Optional)
5082      */
5083     /**
5084      * @cfg {Number} sm (Optional)
5085      */
5086     /**
5087      * @cfg {Number} md (Optional)
5088      */
5089     /**
5090      * @cfg {Number} lg (Optional)
5091      */
5092     /**
5093      * Returns the id of the column at the specified index.
5094      * @param {Number} index The column index
5095      * @return {String} the id
5096      */
5097     getColumnId : function(index){
5098         return this.config[index].id;
5099     },
5100
5101     /**
5102      * Returns the column for a specified id.
5103      * @param {String} id The column id
5104      * @return {Object} the column
5105      */
5106     getColumnById : function(id){
5107         return this.lookup[id];
5108     },
5109
5110     
5111     /**
5112      * Returns the column for a specified dataIndex.
5113      * @param {String} dataIndex The column dataIndex
5114      * @return {Object|Boolean} the column or false if not found
5115      */
5116     getColumnByDataIndex: function(dataIndex){
5117         var index = this.findColumnIndex(dataIndex);
5118         return index > -1 ? this.config[index] : false;
5119     },
5120     
5121     /**
5122      * Returns the index for a specified column id.
5123      * @param {String} id The column id
5124      * @return {Number} the index, or -1 if not found
5125      */
5126     getIndexById : function(id){
5127         for(var i = 0, len = this.config.length; i < len; i++){
5128             if(this.config[i].id == id){
5129                 return i;
5130             }
5131         }
5132         return -1;
5133     },
5134     
5135     /**
5136      * Returns the index for a specified column dataIndex.
5137      * @param {String} dataIndex The column dataIndex
5138      * @return {Number} the index, or -1 if not found
5139      */
5140     
5141     findColumnIndex : function(dataIndex){
5142         for(var i = 0, len = this.config.length; i < len; i++){
5143             if(this.config[i].dataIndex == dataIndex){
5144                 return i;
5145             }
5146         }
5147         return -1;
5148     },
5149     
5150     
5151     moveColumn : function(oldIndex, newIndex){
5152         var c = this.config[oldIndex];
5153         this.config.splice(oldIndex, 1);
5154         this.config.splice(newIndex, 0, c);
5155         this.dataMap = null;
5156         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5157     },
5158
5159     isLocked : function(colIndex){
5160         return this.config[colIndex].locked === true;
5161     },
5162
5163     setLocked : function(colIndex, value, suppressEvent){
5164         if(this.isLocked(colIndex) == value){
5165             return;
5166         }
5167         this.config[colIndex].locked = value;
5168         if(!suppressEvent){
5169             this.fireEvent("columnlockchange", this, colIndex, value);
5170         }
5171     },
5172
5173     getTotalLockedWidth : function(){
5174         var totalWidth = 0;
5175         for(var i = 0; i < this.config.length; i++){
5176             if(this.isLocked(i) && !this.isHidden(i)){
5177                 this.totalWidth += this.getColumnWidth(i);
5178             }
5179         }
5180         return totalWidth;
5181     },
5182
5183     getLockedCount : function(){
5184         for(var i = 0, len = this.config.length; i < len; i++){
5185             if(!this.isLocked(i)){
5186                 return i;
5187             }
5188         }
5189         
5190         return this.config.length;
5191     },
5192
5193     /**
5194      * Returns the number of columns.
5195      * @return {Number}
5196      */
5197     getColumnCount : function(visibleOnly){
5198         if(visibleOnly === true){
5199             var c = 0;
5200             for(var i = 0, len = this.config.length; i < len; i++){
5201                 if(!this.isHidden(i)){
5202                     c++;
5203                 }
5204             }
5205             return c;
5206         }
5207         return this.config.length;
5208     },
5209
5210     /**
5211      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5212      * @param {Function} fn
5213      * @param {Object} scope (optional)
5214      * @return {Array} result
5215      */
5216     getColumnsBy : function(fn, scope){
5217         var r = [];
5218         for(var i = 0, len = this.config.length; i < len; i++){
5219             var c = this.config[i];
5220             if(fn.call(scope||this, c, i) === true){
5221                 r[r.length] = c;
5222             }
5223         }
5224         return r;
5225     },
5226
5227     /**
5228      * Returns true if the specified column is sortable.
5229      * @param {Number} col The column index
5230      * @return {Boolean}
5231      */
5232     isSortable : function(col){
5233         if(typeof this.config[col].sortable == "undefined"){
5234             return this.defaultSortable;
5235         }
5236         return this.config[col].sortable;
5237     },
5238
5239     /**
5240      * Returns the rendering (formatting) function defined for the column.
5241      * @param {Number} col The column index.
5242      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5243      */
5244     getRenderer : function(col){
5245         if(!this.config[col].renderer){
5246             return Roo.grid.ColumnModel.defaultRenderer;
5247         }
5248         return this.config[col].renderer;
5249     },
5250
5251     /**
5252      * Sets the rendering (formatting) function for a column.
5253      * @param {Number} col The column index
5254      * @param {Function} fn The function to use to process the cell's raw data
5255      * to return HTML markup for the grid view. The render function is called with
5256      * the following parameters:<ul>
5257      * <li>Data value.</li>
5258      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5259      * <li>css A CSS style string to apply to the table cell.</li>
5260      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5261      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5262      * <li>Row index</li>
5263      * <li>Column index</li>
5264      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5265      */
5266     setRenderer : function(col, fn){
5267         this.config[col].renderer = fn;
5268     },
5269
5270     /**
5271      * Returns the width for the specified column.
5272      * @param {Number} col The column index
5273      * @return {Number}
5274      */
5275     getColumnWidth : function(col){
5276         return this.config[col].width * 1 || this.defaultWidth;
5277     },
5278
5279     /**
5280      * Sets the width for a column.
5281      * @param {Number} col The column index
5282      * @param {Number} width The new width
5283      */
5284     setColumnWidth : function(col, width, suppressEvent){
5285         this.config[col].width = width;
5286         this.totalWidth = null;
5287         if(!suppressEvent){
5288              this.fireEvent("widthchange", this, col, width);
5289         }
5290     },
5291
5292     /**
5293      * Returns the total width of all columns.
5294      * @param {Boolean} includeHidden True to include hidden column widths
5295      * @return {Number}
5296      */
5297     getTotalWidth : function(includeHidden){
5298         if(!this.totalWidth){
5299             this.totalWidth = 0;
5300             for(var i = 0, len = this.config.length; i < len; i++){
5301                 if(includeHidden || !this.isHidden(i)){
5302                     this.totalWidth += this.getColumnWidth(i);
5303                 }
5304             }
5305         }
5306         return this.totalWidth;
5307     },
5308
5309     /**
5310      * Returns the header for the specified column.
5311      * @param {Number} col The column index
5312      * @return {String}
5313      */
5314     getColumnHeader : function(col){
5315         return this.config[col].header;
5316     },
5317
5318     /**
5319      * Sets the header for a column.
5320      * @param {Number} col The column index
5321      * @param {String} header The new header
5322      */
5323     setColumnHeader : function(col, header){
5324         this.config[col].header = header;
5325         this.fireEvent("headerchange", this, col, header);
5326     },
5327
5328     /**
5329      * Returns the tooltip for the specified column.
5330      * @param {Number} col The column index
5331      * @return {String}
5332      */
5333     getColumnTooltip : function(col){
5334             return this.config[col].tooltip;
5335     },
5336     /**
5337      * Sets the tooltip for a column.
5338      * @param {Number} col The column index
5339      * @param {String} tooltip The new tooltip
5340      */
5341     setColumnTooltip : function(col, tooltip){
5342             this.config[col].tooltip = tooltip;
5343     },
5344
5345     /**
5346      * Returns the dataIndex for the specified column.
5347      * @param {Number} col The column index
5348      * @return {Number}
5349      */
5350     getDataIndex : function(col){
5351         return this.config[col].dataIndex;
5352     },
5353
5354     /**
5355      * Sets the dataIndex for a column.
5356      * @param {Number} col The column index
5357      * @param {Number} dataIndex The new dataIndex
5358      */
5359     setDataIndex : function(col, dataIndex){
5360         this.config[col].dataIndex = dataIndex;
5361     },
5362
5363     
5364     
5365     /**
5366      * Returns true if the cell is editable.
5367      * @param {Number} colIndex The column index
5368      * @param {Number} rowIndex The row index - this is nto actually used..?
5369      * @return {Boolean}
5370      */
5371     isCellEditable : function(colIndex, rowIndex){
5372         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5373     },
5374
5375     /**
5376      * Returns the editor defined for the cell/column.
5377      * return false or null to disable editing.
5378      * @param {Number} colIndex The column index
5379      * @param {Number} rowIndex The row index
5380      * @return {Object}
5381      */
5382     getCellEditor : function(colIndex, rowIndex){
5383         return this.config[colIndex].editor;
5384     },
5385
5386     /**
5387      * Sets if a column is editable.
5388      * @param {Number} col The column index
5389      * @param {Boolean} editable True if the column is editable
5390      */
5391     setEditable : function(col, editable){
5392         this.config[col].editable = editable;
5393     },
5394
5395
5396     /**
5397      * Returns true if the column is hidden.
5398      * @param {Number} colIndex The column index
5399      * @return {Boolean}
5400      */
5401     isHidden : function(colIndex){
5402         return this.config[colIndex].hidden;
5403     },
5404
5405
5406     /**
5407      * Returns true if the column width cannot be changed
5408      */
5409     isFixed : function(colIndex){
5410         return this.config[colIndex].fixed;
5411     },
5412
5413     /**
5414      * Returns true if the column can be resized
5415      * @return {Boolean}
5416      */
5417     isResizable : function(colIndex){
5418         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5419     },
5420     /**
5421      * Sets if a column is hidden.
5422      * @param {Number} colIndex The column index
5423      * @param {Boolean} hidden True if the column is hidden
5424      */
5425     setHidden : function(colIndex, hidden){
5426         this.config[colIndex].hidden = hidden;
5427         this.totalWidth = null;
5428         this.fireEvent("hiddenchange", this, colIndex, hidden);
5429     },
5430
5431     /**
5432      * Sets the editor for a column.
5433      * @param {Number} col The column index
5434      * @param {Object} editor The editor object
5435      */
5436     setEditor : function(col, editor){
5437         this.config[col].editor = editor;
5438     }
5439 });
5440
5441 Roo.grid.ColumnModel.defaultRenderer = function(value){
5442         if(typeof value == "string" && value.length < 1){
5443             return "&#160;";
5444         }
5445         return value;
5446 };
5447
5448 // Alias for backwards compatibility
5449 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5450 /*
5451  * Based on:
5452  * Ext JS Library 1.1.1
5453  * Copyright(c) 2006-2007, Ext JS, LLC.
5454  *
5455  * Originally Released Under LGPL - original licence link has changed is not relivant.
5456  *
5457  * Fork - LGPL
5458  * <script type="text/javascript">
5459  */
5460  
5461 /**
5462  * @class Roo.LoadMask
5463  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5464  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5465  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5466  * element's UpdateManager load indicator and will be destroyed after the initial load.
5467  * @constructor
5468  * Create a new LoadMask
5469  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5470  * @param {Object} config The config object
5471  */
5472 Roo.LoadMask = function(el, config){
5473     this.el = Roo.get(el);
5474     Roo.apply(this, config);
5475     if(this.store){
5476         this.store.on('beforeload', this.onBeforeLoad, this);
5477         this.store.on('load', this.onLoad, this);
5478         this.store.on('loadexception', this.onLoadException, this);
5479         this.removeMask = false;
5480     }else{
5481         var um = this.el.getUpdateManager();
5482         um.showLoadIndicator = false; // disable the default indicator
5483         um.on('beforeupdate', this.onBeforeLoad, this);
5484         um.on('update', this.onLoad, this);
5485         um.on('failure', this.onLoad, this);
5486         this.removeMask = true;
5487     }
5488 };
5489
5490 Roo.LoadMask.prototype = {
5491     /**
5492      * @cfg {Boolean} removeMask
5493      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5494      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5495      */
5496     /**
5497      * @cfg {String} msg
5498      * The text to display in a centered loading message box (defaults to 'Loading...')
5499      */
5500     msg : 'Loading...',
5501     /**
5502      * @cfg {String} msgCls
5503      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5504      */
5505     msgCls : 'x-mask-loading',
5506
5507     /**
5508      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5509      * @type Boolean
5510      */
5511     disabled: false,
5512
5513     /**
5514      * Disables the mask to prevent it from being displayed
5515      */
5516     disable : function(){
5517        this.disabled = true;
5518     },
5519
5520     /**
5521      * Enables the mask so that it can be displayed
5522      */
5523     enable : function(){
5524         this.disabled = false;
5525     },
5526     
5527     onLoadException : function()
5528     {
5529         Roo.log(arguments);
5530         
5531         if (typeof(arguments[3]) != 'undefined') {
5532             Roo.MessageBox.alert("Error loading",arguments[3]);
5533         } 
5534         /*
5535         try {
5536             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5537                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5538             }   
5539         } catch(e) {
5540             
5541         }
5542         */
5543     
5544         
5545         
5546         this.el.unmask(this.removeMask);
5547     },
5548     // private
5549     onLoad : function()
5550     {
5551         this.el.unmask(this.removeMask);
5552     },
5553
5554     // private
5555     onBeforeLoad : function(){
5556         if(!this.disabled){
5557             this.el.mask(this.msg, this.msgCls);
5558         }
5559     },
5560
5561     // private
5562     destroy : function(){
5563         if(this.store){
5564             this.store.un('beforeload', this.onBeforeLoad, this);
5565             this.store.un('load', this.onLoad, this);
5566             this.store.un('loadexception', this.onLoadException, this);
5567         }else{
5568             var um = this.el.getUpdateManager();
5569             um.un('beforeupdate', this.onBeforeLoad, this);
5570             um.un('update', this.onLoad, this);
5571             um.un('failure', this.onLoad, this);
5572         }
5573     }
5574 };/*
5575  * - LGPL
5576  *
5577  * table
5578  * 
5579  */
5580
5581 /**
5582  * @class Roo.bootstrap.Table
5583  * @extends Roo.bootstrap.Component
5584  * Bootstrap Table class
5585  * @cfg {String} cls table class
5586  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5587  * @cfg {String} bgcolor Specifies the background color for a table
5588  * @cfg {Number} border Specifies whether the table cells should have borders or not
5589  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5590  * @cfg {Number} cellspacing Specifies the space between cells
5591  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5592  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5593  * @cfg {String} sortable Specifies that the table should be sortable
5594  * @cfg {String} summary Specifies a summary of the content of a table
5595  * @cfg {Number} width Specifies the width of a table
5596  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5597  * 
5598  * @cfg {boolean} striped Should the rows be alternative striped
5599  * @cfg {boolean} bordered Add borders to the table
5600  * @cfg {boolean} hover Add hover highlighting
5601  * @cfg {boolean} condensed Format condensed
5602  * @cfg {boolean} responsive Format condensed
5603  * @cfg {Boolean} loadMask (true|false) default false
5604  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5605  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5606  * @cfg {Boolean} rowSelection (true|false) default false
5607  * @cfg {Boolean} cellSelection (true|false) default false
5608  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5609  
5610  * 
5611  * @constructor
5612  * Create a new Table
5613  * @param {Object} config The config object
5614  */
5615
5616 Roo.bootstrap.Table = function(config){
5617     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5618     
5619     // BC...
5620     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5621     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5622     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5623     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5624     
5625     
5626     if (this.sm) {
5627         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5628         this.sm = this.selModel;
5629         this.sm.xmodule = this.xmodule || false;
5630     }
5631     if (this.cm && typeof(this.cm.config) == 'undefined') {
5632         this.colModel = new Roo.grid.ColumnModel(this.cm);
5633         this.cm = this.colModel;
5634         this.cm.xmodule = this.xmodule || false;
5635     }
5636     if (this.store) {
5637         this.store= Roo.factory(this.store, Roo.data);
5638         this.ds = this.store;
5639         this.ds.xmodule = this.xmodule || false;
5640          
5641     }
5642     if (this.footer && this.store) {
5643         this.footer.dataSource = this.ds;
5644         this.footer = Roo.factory(this.footer);
5645     }
5646     
5647     /** @private */
5648     this.addEvents({
5649         /**
5650          * @event cellclick
5651          * Fires when a cell is clicked
5652          * @param {Roo.bootstrap.Table} this
5653          * @param {Roo.Element} el
5654          * @param {Number} rowIndex
5655          * @param {Number} columnIndex
5656          * @param {Roo.EventObject} e
5657          */
5658         "cellclick" : true,
5659         /**
5660          * @event celldblclick
5661          * Fires when a cell is double clicked
5662          * @param {Roo.bootstrap.Table} this
5663          * @param {Roo.Element} el
5664          * @param {Number} rowIndex
5665          * @param {Number} columnIndex
5666          * @param {Roo.EventObject} e
5667          */
5668         "celldblclick" : true,
5669         /**
5670          * @event rowclick
5671          * Fires when a row is clicked
5672          * @param {Roo.bootstrap.Table} this
5673          * @param {Roo.Element} el
5674          * @param {Number} rowIndex
5675          * @param {Roo.EventObject} e
5676          */
5677         "rowclick" : true,
5678         /**
5679          * @event rowdblclick
5680          * Fires when a row is double clicked
5681          * @param {Roo.bootstrap.Table} this
5682          * @param {Roo.Element} el
5683          * @param {Number} rowIndex
5684          * @param {Roo.EventObject} e
5685          */
5686         "rowdblclick" : true,
5687         /**
5688          * @event mouseover
5689          * Fires when a mouseover occur
5690          * @param {Roo.bootstrap.Table} this
5691          * @param {Roo.Element} el
5692          * @param {Number} rowIndex
5693          * @param {Number} columnIndex
5694          * @param {Roo.EventObject} e
5695          */
5696         "mouseover" : true,
5697         /**
5698          * @event mouseout
5699          * Fires when a mouseout occur
5700          * @param {Roo.bootstrap.Table} this
5701          * @param {Roo.Element} el
5702          * @param {Number} rowIndex
5703          * @param {Number} columnIndex
5704          * @param {Roo.EventObject} e
5705          */
5706         "mouseout" : true,
5707         /**
5708          * @event rowclass
5709          * Fires when a row is rendered, so you can change add a style to it.
5710          * @param {Roo.bootstrap.Table} this
5711          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5712          */
5713         'rowclass' : true,
5714           /**
5715          * @event rowsrendered
5716          * Fires when all the  rows have been rendered
5717          * @param {Roo.bootstrap.Table} this
5718          */
5719         'rowsrendered' : true
5720         
5721     });
5722 };
5723
5724 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5725     
5726     cls: false,
5727     align: false,
5728     bgcolor: false,
5729     border: false,
5730     cellpadding: false,
5731     cellspacing: false,
5732     frame: false,
5733     rules: false,
5734     sortable: false,
5735     summary: false,
5736     width: false,
5737     striped : false,
5738     bordered: false,
5739     hover:  false,
5740     condensed : false,
5741     responsive : false,
5742     sm : false,
5743     cm : false,
5744     store : false,
5745     loadMask : false,
5746     footerShow : true,
5747     headerShow : true,
5748   
5749     rowSelection : false,
5750     cellSelection : false,
5751     layout : false,
5752     
5753     // Roo.Element - the tbody
5754     mainBody: false, 
5755     
5756     getAutoCreate : function(){
5757         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5758         
5759         cfg = {
5760             tag: 'table',
5761             cls : 'table',
5762             cn : []
5763         };
5764             
5765         if (this.striped) {
5766             cfg.cls += ' table-striped';
5767         }
5768         
5769         if (this.hover) {
5770             cfg.cls += ' table-hover';
5771         }
5772         if (this.bordered) {
5773             cfg.cls += ' table-bordered';
5774         }
5775         if (this.condensed) {
5776             cfg.cls += ' table-condensed';
5777         }
5778         if (this.responsive) {
5779             cfg.cls += ' table-responsive';
5780         }
5781         
5782         if (this.cls) {
5783             cfg.cls+=  ' ' +this.cls;
5784         }
5785         
5786         // this lot should be simplifed...
5787         
5788         if (this.align) {
5789             cfg.align=this.align;
5790         }
5791         if (this.bgcolor) {
5792             cfg.bgcolor=this.bgcolor;
5793         }
5794         if (this.border) {
5795             cfg.border=this.border;
5796         }
5797         if (this.cellpadding) {
5798             cfg.cellpadding=this.cellpadding;
5799         }
5800         if (this.cellspacing) {
5801             cfg.cellspacing=this.cellspacing;
5802         }
5803         if (this.frame) {
5804             cfg.frame=this.frame;
5805         }
5806         if (this.rules) {
5807             cfg.rules=this.rules;
5808         }
5809         if (this.sortable) {
5810             cfg.sortable=this.sortable;
5811         }
5812         if (this.summary) {
5813             cfg.summary=this.summary;
5814         }
5815         if (this.width) {
5816             cfg.width=this.width;
5817         }
5818         if (this.layout) {
5819             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5820         }
5821         
5822         if(this.store || this.cm){
5823             if(this.headerShow){
5824                 cfg.cn.push(this.renderHeader());
5825             }
5826             
5827             cfg.cn.push(this.renderBody());
5828             
5829             if(this.footerShow){
5830                 cfg.cn.push(this.renderFooter());
5831             }
5832             
5833             cfg.cls+=  ' TableGrid';
5834         }
5835         
5836         return { cn : [ cfg ] };
5837     },
5838     
5839     initEvents : function()
5840     {   
5841         if(!this.store || !this.cm){
5842             return;
5843         }
5844         
5845         //Roo.log('initEvents with ds!!!!');
5846         
5847         this.mainBody = this.el.select('tbody', true).first();
5848         
5849         
5850         var _this = this;
5851         
5852         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5853             e.on('click', _this.sort, _this);
5854         });
5855         
5856         this.el.on("click", this.onClick, this);
5857         this.el.on("dblclick", this.onDblClick, this);
5858         
5859         // why is this done????? = it breaks dialogs??
5860         //this.parent().el.setStyle('position', 'relative');
5861         
5862         
5863         if (this.footer) {
5864             this.footer.parentId = this.id;
5865             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5866         }
5867         
5868         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5869         
5870         this.store.on('load', this.onLoad, this);
5871         this.store.on('beforeload', this.onBeforeLoad, this);
5872         this.store.on('update', this.onUpdate, this);
5873         this.store.on('add', this.onAdd, this);
5874         
5875     },
5876     
5877     onMouseover : function(e, el)
5878     {
5879         var cell = Roo.get(el);
5880         
5881         if(!cell){
5882             return;
5883         }
5884         
5885         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5886             cell = cell.findParent('td', false, true);
5887         }
5888         
5889         var row = cell.findParent('tr', false, true);
5890         var cellIndex = cell.dom.cellIndex;
5891         var rowIndex = row.dom.rowIndex - 1; // start from 0
5892         
5893         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5894         
5895     },
5896     
5897     onMouseout : function(e, el)
5898     {
5899         var cell = Roo.get(el);
5900         
5901         if(!cell){
5902             return;
5903         }
5904         
5905         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5906             cell = cell.findParent('td', false, true);
5907         }
5908         
5909         var row = cell.findParent('tr', false, true);
5910         var cellIndex = cell.dom.cellIndex;
5911         var rowIndex = row.dom.rowIndex - 1; // start from 0
5912         
5913         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5914         
5915     },
5916     
5917     onClick : function(e, el)
5918     {
5919         var cell = Roo.get(el);
5920         
5921         if(!cell || (!this.cellSelection && !this.rowSelection)){
5922             return;
5923         }
5924         
5925         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5926             cell = cell.findParent('td', false, true);
5927         }
5928         
5929         if(!cell || typeof(cell) == 'undefined'){
5930             return;
5931         }
5932         
5933         var row = cell.findParent('tr', false, true);
5934         
5935         if(!row || typeof(row) == 'undefined'){
5936             return;
5937         }
5938         
5939         var cellIndex = cell.dom.cellIndex;
5940         var rowIndex = this.getRowIndex(row);
5941         
5942         // why??? - should these not be based on SelectionModel?
5943         if(this.cellSelection){
5944             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5945         }
5946         
5947         if(this.rowSelection){
5948             this.fireEvent('rowclick', this, row, rowIndex, e);
5949         }
5950         
5951         
5952     },
5953     
5954     onDblClick : function(e,el)
5955     {
5956         var cell = Roo.get(el);
5957         
5958         if(!cell || (!this.CellSelection && !this.RowSelection)){
5959             return;
5960         }
5961         
5962         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5963             cell = cell.findParent('td', false, true);
5964         }
5965         
5966         if(!cell || typeof(cell) == 'undefined'){
5967             return;
5968         }
5969         
5970         var row = cell.findParent('tr', false, true);
5971         
5972         if(!row || typeof(row) == 'undefined'){
5973             return;
5974         }
5975         
5976         var cellIndex = cell.dom.cellIndex;
5977         var rowIndex = this.getRowIndex(row);
5978         
5979         if(this.CellSelection){
5980             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5981         }
5982         
5983         if(this.RowSelection){
5984             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5985         }
5986     },
5987     
5988     sort : function(e,el)
5989     {
5990         var col = Roo.get(el);
5991         
5992         if(!col.hasClass('sortable')){
5993             return;
5994         }
5995         
5996         var sort = col.attr('sort');
5997         var dir = 'ASC';
5998         
5999         if(col.hasClass('glyphicon-arrow-up')){
6000             dir = 'DESC';
6001         }
6002         
6003         this.store.sortInfo = {field : sort, direction : dir};
6004         
6005         if (this.footer) {
6006             Roo.log("calling footer first");
6007             this.footer.onClick('first');
6008         } else {
6009         
6010             this.store.load({ params : { start : 0 } });
6011         }
6012     },
6013     
6014     renderHeader : function()
6015     {
6016         var header = {
6017             tag: 'thead',
6018             cn : []
6019         };
6020         
6021         var cm = this.cm;
6022         
6023         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6024             
6025             var config = cm.config[i];
6026             
6027             var c = {
6028                 tag: 'th',
6029                 style : '',
6030                 html: cm.getColumnHeader(i)
6031             };
6032             
6033             var hh = '';
6034             
6035             if(typeof(config.lgHeader) != 'undefined'){
6036                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6037             }
6038             
6039             if(typeof(config.mdHeader) != 'undefined'){
6040                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6041             }
6042             
6043             if(typeof(config.smHeader) != 'undefined'){
6044                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6045             }
6046             
6047             if(typeof(config.xsHeader) != 'undefined'){
6048                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6049             }
6050             
6051             if(hh.length){
6052                 c.html = hh;
6053             }
6054             
6055             if(typeof(config.tooltip) != 'undefined'){
6056                 c.tooltip = config.tooltip;
6057             }
6058             
6059             if(typeof(config.colspan) != 'undefined'){
6060                 c.colspan = config.colspan;
6061             }
6062             
6063             if(typeof(config.hidden) != 'undefined' && config.hidden){
6064                 c.style += ' display:none;';
6065             }
6066             
6067             if(typeof(config.dataIndex) != 'undefined'){
6068                 c.sort = config.dataIndex;
6069             }
6070             
6071             if(typeof(config.sortable) != 'undefined' && config.sortable){
6072                 c.cls = 'sortable';
6073             }
6074             
6075             if(typeof(config.align) != 'undefined' && config.align.length){
6076                 c.style += ' text-align:' + config.align + ';';
6077             }
6078             
6079             if(typeof(config.width) != 'undefined'){
6080                 c.style += ' width:' + config.width + 'px;';
6081             }
6082             
6083             if(typeof(config.cls) != 'undefined'){
6084                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6085             }
6086             
6087             ['xs','sm','md','lg'].map(function(size){
6088                 
6089                 if(typeof(config[size]) == 'undefined'){
6090                     return;
6091                 }
6092                 
6093                 if (!config[size]) { // 0 = hidden
6094                     c.cls += ' hidden-' + size;
6095                     return;
6096                 }
6097                 
6098                 c.cls += ' col-' + size + '-' + config[size];
6099
6100             });
6101             
6102             header.cn.push(c)
6103         }
6104         
6105         return header;
6106     },
6107     
6108     renderBody : function()
6109     {
6110         var body = {
6111             tag: 'tbody',
6112             cn : [
6113                 {
6114                     tag: 'tr',
6115                     cn : [
6116                         {
6117                             tag : 'td',
6118                             colspan :  this.cm.getColumnCount()
6119                         }
6120                     ]
6121                 }
6122             ]
6123         };
6124         
6125         return body;
6126     },
6127     
6128     renderFooter : function()
6129     {
6130         var footer = {
6131             tag: 'tfoot',
6132             cn : [
6133                 {
6134                     tag: 'tr',
6135                     cn : [
6136                         {
6137                             tag : 'td',
6138                             colspan :  this.cm.getColumnCount()
6139                         }
6140                     ]
6141                 }
6142             ]
6143         };
6144         
6145         return footer;
6146     },
6147     
6148     
6149     
6150     onLoad : function()
6151     {
6152 //        Roo.log('ds onload');
6153         this.clear();
6154         
6155         var _this = this;
6156         var cm = this.cm;
6157         var ds = this.store;
6158         
6159         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6160             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6161             
6162             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6163                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6164             }
6165             
6166             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6167                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6168             }
6169         });
6170         
6171         var tbody =  this.mainBody;
6172               
6173         if(ds.getCount() > 0){
6174             ds.data.each(function(d,rowIndex){
6175                 var row =  this.renderRow(cm, ds, rowIndex);
6176                 
6177                 tbody.createChild(row);
6178                 
6179                 var _this = this;
6180                 
6181                 if(row.cellObjects.length){
6182                     Roo.each(row.cellObjects, function(r){
6183                         _this.renderCellObject(r);
6184                     })
6185                 }
6186                 
6187             }, this);
6188         }
6189         
6190         Roo.each(this.el.select('tbody td', true).elements, function(e){
6191             e.on('mouseover', _this.onMouseover, _this);
6192         });
6193         
6194         Roo.each(this.el.select('tbody td', true).elements, function(e){
6195             e.on('mouseout', _this.onMouseout, _this);
6196         });
6197         this.fireEvent('rowsrendered', this);
6198         //if(this.loadMask){
6199         //    this.maskEl.hide();
6200         //}
6201     },
6202     
6203     
6204     onUpdate : function(ds,record)
6205     {
6206         this.refreshRow(record);
6207     },
6208     
6209     onRemove : function(ds, record, index, isUpdate){
6210         if(isUpdate !== true){
6211             this.fireEvent("beforerowremoved", this, index, record);
6212         }
6213         var bt = this.mainBody.dom;
6214         
6215         var rows = this.el.select('tbody > tr', true).elements;
6216         
6217         if(typeof(rows[index]) != 'undefined'){
6218             bt.removeChild(rows[index].dom);
6219         }
6220         
6221 //        if(bt.rows[index]){
6222 //            bt.removeChild(bt.rows[index]);
6223 //        }
6224         
6225         if(isUpdate !== true){
6226             //this.stripeRows(index);
6227             //this.syncRowHeights(index, index);
6228             //this.layout();
6229             this.fireEvent("rowremoved", this, index, record);
6230         }
6231     },
6232     
6233     onAdd : function(ds, records, rowIndex)
6234     {
6235         //Roo.log('on Add called');
6236         // - note this does not handle multiple adding very well..
6237         var bt = this.mainBody.dom;
6238         for (var i =0 ; i < records.length;i++) {
6239             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6240             //Roo.log(records[i]);
6241             //Roo.log(this.store.getAt(rowIndex+i));
6242             this.insertRow(this.store, rowIndex + i, false);
6243             return;
6244         }
6245         
6246     },
6247     
6248     
6249     refreshRow : function(record){
6250         var ds = this.store, index;
6251         if(typeof record == 'number'){
6252             index = record;
6253             record = ds.getAt(index);
6254         }else{
6255             index = ds.indexOf(record);
6256         }
6257         this.insertRow(ds, index, true);
6258         this.onRemove(ds, record, index+1, true);
6259         //this.syncRowHeights(index, index);
6260         //this.layout();
6261         this.fireEvent("rowupdated", this, index, record);
6262     },
6263     
6264     insertRow : function(dm, rowIndex, isUpdate){
6265         
6266         if(!isUpdate){
6267             this.fireEvent("beforerowsinserted", this, rowIndex);
6268         }
6269             //var s = this.getScrollState();
6270         var row = this.renderRow(this.cm, this.store, rowIndex);
6271         // insert before rowIndex..
6272         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6273         
6274         var _this = this;
6275                 
6276         if(row.cellObjects.length){
6277             Roo.each(row.cellObjects, function(r){
6278                 _this.renderCellObject(r);
6279             })
6280         }
6281             
6282         if(!isUpdate){
6283             this.fireEvent("rowsinserted", this, rowIndex);
6284             //this.syncRowHeights(firstRow, lastRow);
6285             //this.stripeRows(firstRow);
6286             //this.layout();
6287         }
6288         
6289     },
6290     
6291     
6292     getRowDom : function(rowIndex)
6293     {
6294         var rows = this.el.select('tbody > tr', true).elements;
6295         
6296         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6297         
6298     },
6299     // returns the object tree for a tr..
6300   
6301     
6302     renderRow : function(cm, ds, rowIndex) 
6303     {
6304         
6305         var d = ds.getAt(rowIndex);
6306         
6307         var row = {
6308             tag : 'tr',
6309             cn : []
6310         };
6311             
6312         var cellObjects = [];
6313         
6314         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6315             var config = cm.config[i];
6316             
6317             var renderer = cm.getRenderer(i);
6318             var value = '';
6319             var id = false;
6320             
6321             if(typeof(renderer) !== 'undefined'){
6322                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6323             }
6324             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6325             // and are rendered into the cells after the row is rendered - using the id for the element.
6326             
6327             if(typeof(value) === 'object'){
6328                 id = Roo.id();
6329                 cellObjects.push({
6330                     container : id,
6331                     cfg : value 
6332                 })
6333             }
6334             
6335             var rowcfg = {
6336                 record: d,
6337                 rowIndex : rowIndex,
6338                 colIndex : i,
6339                 rowClass : ''
6340             };
6341
6342             this.fireEvent('rowclass', this, rowcfg);
6343             
6344             var td = {
6345                 tag: 'td',
6346                 cls : rowcfg.rowClass,
6347                 style: '',
6348                 html: (typeof(value) === 'object') ? '' : value
6349             };
6350             
6351             if (id) {
6352                 td.id = id;
6353             }
6354             
6355             if(typeof(config.colspan) != 'undefined'){
6356                 td.colspan = config.colspan;
6357             }
6358             
6359             if(typeof(config.hidden) != 'undefined' && config.hidden){
6360                 td.style += ' display:none;';
6361             }
6362             
6363             if(typeof(config.align) != 'undefined' && config.align.length){
6364                 td.style += ' text-align:' + config.align + ';';
6365             }
6366             
6367             if(typeof(config.width) != 'undefined'){
6368                 td.style += ' width:' +  config.width + 'px;';
6369             }
6370             
6371             if(typeof(config.cursor) != 'undefined'){
6372                 td.style += ' cursor:' +  config.cursor + ';';
6373             }
6374             
6375             if(typeof(config.cls) != 'undefined'){
6376                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6377             }
6378             
6379             ['xs','sm','md','lg'].map(function(size){
6380                 
6381                 if(typeof(config[size]) == 'undefined'){
6382                     return;
6383                 }
6384                 
6385                 if (!config[size]) { // 0 = hidden
6386                     td.cls += ' hidden-' + size;
6387                     return;
6388                 }
6389                 
6390                 td.cls += ' col-' + size + '-' + config[size];
6391
6392             });
6393              
6394             row.cn.push(td);
6395            
6396         }
6397         
6398         row.cellObjects = cellObjects;
6399         
6400         return row;
6401           
6402     },
6403     
6404     
6405     
6406     onBeforeLoad : function()
6407     {
6408         //Roo.log('ds onBeforeLoad');
6409         
6410         //this.clear();
6411         
6412         //if(this.loadMask){
6413         //    this.maskEl.show();
6414         //}
6415     },
6416      /**
6417      * Remove all rows
6418      */
6419     clear : function()
6420     {
6421         this.el.select('tbody', true).first().dom.innerHTML = '';
6422     },
6423     /**
6424      * Show or hide a row.
6425      * @param {Number} rowIndex to show or hide
6426      * @param {Boolean} state hide
6427      */
6428     setRowVisibility : function(rowIndex, state)
6429     {
6430         var bt = this.mainBody.dom;
6431         
6432         var rows = this.el.select('tbody > tr', true).elements;
6433         
6434         if(typeof(rows[rowIndex]) == 'undefined'){
6435             return;
6436         }
6437         rows[rowIndex].dom.style.display = state ? '' : 'none';
6438     },
6439     
6440     
6441     getSelectionModel : function(){
6442         if(!this.selModel){
6443             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6444         }
6445         return this.selModel;
6446     },
6447     /*
6448      * Render the Roo.bootstrap object from renderder
6449      */
6450     renderCellObject : function(r)
6451     {
6452         var _this = this;
6453         
6454         var t = r.cfg.render(r.container);
6455         
6456         if(r.cfg.cn){
6457             Roo.each(r.cfg.cn, function(c){
6458                 var child = {
6459                     container: t.getChildContainer(),
6460                     cfg: c
6461                 };
6462                 _this.renderCellObject(child);
6463             })
6464         }
6465     },
6466     
6467     getRowIndex : function(row)
6468     {
6469         var rowIndex = -1;
6470         
6471         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6472             if(el != row){
6473                 return;
6474             }
6475             
6476             rowIndex = index;
6477         });
6478         
6479         return rowIndex;
6480     }
6481    
6482 });
6483
6484  
6485
6486  /*
6487  * - LGPL
6488  *
6489  * table cell
6490  * 
6491  */
6492
6493 /**
6494  * @class Roo.bootstrap.TableCell
6495  * @extends Roo.bootstrap.Component
6496  * Bootstrap TableCell class
6497  * @cfg {String} html cell contain text
6498  * @cfg {String} cls cell class
6499  * @cfg {String} tag cell tag (td|th) default td
6500  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6501  * @cfg {String} align Aligns the content in a cell
6502  * @cfg {String} axis Categorizes cells
6503  * @cfg {String} bgcolor Specifies the background color of a cell
6504  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6505  * @cfg {Number} colspan Specifies the number of columns a cell should span
6506  * @cfg {String} headers Specifies one or more header cells a cell is related to
6507  * @cfg {Number} height Sets the height of a cell
6508  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6509  * @cfg {Number} rowspan Sets the number of rows a cell should span
6510  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6511  * @cfg {String} valign Vertical aligns the content in a cell
6512  * @cfg {Number} width Specifies the width of a cell
6513  * 
6514  * @constructor
6515  * Create a new TableCell
6516  * @param {Object} config The config object
6517  */
6518
6519 Roo.bootstrap.TableCell = function(config){
6520     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6521 };
6522
6523 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6524     
6525     html: false,
6526     cls: false,
6527     tag: false,
6528     abbr: false,
6529     align: false,
6530     axis: false,
6531     bgcolor: false,
6532     charoff: false,
6533     colspan: false,
6534     headers: false,
6535     height: false,
6536     nowrap: false,
6537     rowspan: false,
6538     scope: false,
6539     valign: false,
6540     width: false,
6541     
6542     
6543     getAutoCreate : function(){
6544         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6545         
6546         cfg = {
6547             tag: 'td'
6548         };
6549         
6550         if(this.tag){
6551             cfg.tag = this.tag;
6552         }
6553         
6554         if (this.html) {
6555             cfg.html=this.html
6556         }
6557         if (this.cls) {
6558             cfg.cls=this.cls
6559         }
6560         if (this.abbr) {
6561             cfg.abbr=this.abbr
6562         }
6563         if (this.align) {
6564             cfg.align=this.align
6565         }
6566         if (this.axis) {
6567             cfg.axis=this.axis
6568         }
6569         if (this.bgcolor) {
6570             cfg.bgcolor=this.bgcolor
6571         }
6572         if (this.charoff) {
6573             cfg.charoff=this.charoff
6574         }
6575         if (this.colspan) {
6576             cfg.colspan=this.colspan
6577         }
6578         if (this.headers) {
6579             cfg.headers=this.headers
6580         }
6581         if (this.height) {
6582             cfg.height=this.height
6583         }
6584         if (this.nowrap) {
6585             cfg.nowrap=this.nowrap
6586         }
6587         if (this.rowspan) {
6588             cfg.rowspan=this.rowspan
6589         }
6590         if (this.scope) {
6591             cfg.scope=this.scope
6592         }
6593         if (this.valign) {
6594             cfg.valign=this.valign
6595         }
6596         if (this.width) {
6597             cfg.width=this.width
6598         }
6599         
6600         
6601         return cfg;
6602     }
6603    
6604 });
6605
6606  
6607
6608  /*
6609  * - LGPL
6610  *
6611  * table row
6612  * 
6613  */
6614
6615 /**
6616  * @class Roo.bootstrap.TableRow
6617  * @extends Roo.bootstrap.Component
6618  * Bootstrap TableRow class
6619  * @cfg {String} cls row class
6620  * @cfg {String} align Aligns the content in a table row
6621  * @cfg {String} bgcolor Specifies a background color for a table row
6622  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6623  * @cfg {String} valign Vertical aligns the content in a table row
6624  * 
6625  * @constructor
6626  * Create a new TableRow
6627  * @param {Object} config The config object
6628  */
6629
6630 Roo.bootstrap.TableRow = function(config){
6631     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6632 };
6633
6634 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6635     
6636     cls: false,
6637     align: false,
6638     bgcolor: false,
6639     charoff: false,
6640     valign: false,
6641     
6642     getAutoCreate : function(){
6643         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6644         
6645         cfg = {
6646             tag: 'tr'
6647         };
6648             
6649         if(this.cls){
6650             cfg.cls = this.cls;
6651         }
6652         if(this.align){
6653             cfg.align = this.align;
6654         }
6655         if(this.bgcolor){
6656             cfg.bgcolor = this.bgcolor;
6657         }
6658         if(this.charoff){
6659             cfg.charoff = this.charoff;
6660         }
6661         if(this.valign){
6662             cfg.valign = this.valign;
6663         }
6664         
6665         return cfg;
6666     }
6667    
6668 });
6669
6670  
6671
6672  /*
6673  * - LGPL
6674  *
6675  * table body
6676  * 
6677  */
6678
6679 /**
6680  * @class Roo.bootstrap.TableBody
6681  * @extends Roo.bootstrap.Component
6682  * Bootstrap TableBody class
6683  * @cfg {String} cls element class
6684  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6685  * @cfg {String} align Aligns the content inside the element
6686  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6687  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6688  * 
6689  * @constructor
6690  * Create a new TableBody
6691  * @param {Object} config The config object
6692  */
6693
6694 Roo.bootstrap.TableBody = function(config){
6695     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6696 };
6697
6698 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6699     
6700     cls: false,
6701     tag: false,
6702     align: false,
6703     charoff: false,
6704     valign: false,
6705     
6706     getAutoCreate : function(){
6707         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6708         
6709         cfg = {
6710             tag: 'tbody'
6711         };
6712             
6713         if (this.cls) {
6714             cfg.cls=this.cls
6715         }
6716         if(this.tag){
6717             cfg.tag = this.tag;
6718         }
6719         
6720         if(this.align){
6721             cfg.align = this.align;
6722         }
6723         if(this.charoff){
6724             cfg.charoff = this.charoff;
6725         }
6726         if(this.valign){
6727             cfg.valign = this.valign;
6728         }
6729         
6730         return cfg;
6731     }
6732     
6733     
6734 //    initEvents : function()
6735 //    {
6736 //        
6737 //        if(!this.store){
6738 //            return;
6739 //        }
6740 //        
6741 //        this.store = Roo.factory(this.store, Roo.data);
6742 //        this.store.on('load', this.onLoad, this);
6743 //        
6744 //        this.store.load();
6745 //        
6746 //    },
6747 //    
6748 //    onLoad: function () 
6749 //    {   
6750 //        this.fireEvent('load', this);
6751 //    }
6752 //    
6753 //   
6754 });
6755
6756  
6757
6758  /*
6759  * Based on:
6760  * Ext JS Library 1.1.1
6761  * Copyright(c) 2006-2007, Ext JS, LLC.
6762  *
6763  * Originally Released Under LGPL - original licence link has changed is not relivant.
6764  *
6765  * Fork - LGPL
6766  * <script type="text/javascript">
6767  */
6768
6769 // as we use this in bootstrap.
6770 Roo.namespace('Roo.form');
6771  /**
6772  * @class Roo.form.Action
6773  * Internal Class used to handle form actions
6774  * @constructor
6775  * @param {Roo.form.BasicForm} el The form element or its id
6776  * @param {Object} config Configuration options
6777  */
6778
6779  
6780  
6781 // define the action interface
6782 Roo.form.Action = function(form, options){
6783     this.form = form;
6784     this.options = options || {};
6785 };
6786 /**
6787  * Client Validation Failed
6788  * @const 
6789  */
6790 Roo.form.Action.CLIENT_INVALID = 'client';
6791 /**
6792  * Server Validation Failed
6793  * @const 
6794  */
6795 Roo.form.Action.SERVER_INVALID = 'server';
6796  /**
6797  * Connect to Server Failed
6798  * @const 
6799  */
6800 Roo.form.Action.CONNECT_FAILURE = 'connect';
6801 /**
6802  * Reading Data from Server Failed
6803  * @const 
6804  */
6805 Roo.form.Action.LOAD_FAILURE = 'load';
6806
6807 Roo.form.Action.prototype = {
6808     type : 'default',
6809     failureType : undefined,
6810     response : undefined,
6811     result : undefined,
6812
6813     // interface method
6814     run : function(options){
6815
6816     },
6817
6818     // interface method
6819     success : function(response){
6820
6821     },
6822
6823     // interface method
6824     handleResponse : function(response){
6825
6826     },
6827
6828     // default connection failure
6829     failure : function(response){
6830         
6831         this.response = response;
6832         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6833         this.form.afterAction(this, false);
6834     },
6835
6836     processResponse : function(response){
6837         this.response = response;
6838         if(!response.responseText){
6839             return true;
6840         }
6841         this.result = this.handleResponse(response);
6842         return this.result;
6843     },
6844
6845     // utility functions used internally
6846     getUrl : function(appendParams){
6847         var url = this.options.url || this.form.url || this.form.el.dom.action;
6848         if(appendParams){
6849             var p = this.getParams();
6850             if(p){
6851                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6852             }
6853         }
6854         return url;
6855     },
6856
6857     getMethod : function(){
6858         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6859     },
6860
6861     getParams : function(){
6862         var bp = this.form.baseParams;
6863         var p = this.options.params;
6864         if(p){
6865             if(typeof p == "object"){
6866                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6867             }else if(typeof p == 'string' && bp){
6868                 p += '&' + Roo.urlEncode(bp);
6869             }
6870         }else if(bp){
6871             p = Roo.urlEncode(bp);
6872         }
6873         return p;
6874     },
6875
6876     createCallback : function(){
6877         return {
6878             success: this.success,
6879             failure: this.failure,
6880             scope: this,
6881             timeout: (this.form.timeout*1000),
6882             upload: this.form.fileUpload ? this.success : undefined
6883         };
6884     }
6885 };
6886
6887 Roo.form.Action.Submit = function(form, options){
6888     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6889 };
6890
6891 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6892     type : 'submit',
6893
6894     haveProgress : false,
6895     uploadComplete : false,
6896     
6897     // uploadProgress indicator.
6898     uploadProgress : function()
6899     {
6900         if (!this.form.progressUrl) {
6901             return;
6902         }
6903         
6904         if (!this.haveProgress) {
6905             Roo.MessageBox.progress("Uploading", "Uploading");
6906         }
6907         if (this.uploadComplete) {
6908            Roo.MessageBox.hide();
6909            return;
6910         }
6911         
6912         this.haveProgress = true;
6913    
6914         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6915         
6916         var c = new Roo.data.Connection();
6917         c.request({
6918             url : this.form.progressUrl,
6919             params: {
6920                 id : uid
6921             },
6922             method: 'GET',
6923             success : function(req){
6924                //console.log(data);
6925                 var rdata = false;
6926                 var edata;
6927                 try  {
6928                    rdata = Roo.decode(req.responseText)
6929                 } catch (e) {
6930                     Roo.log("Invalid data from server..");
6931                     Roo.log(edata);
6932                     return;
6933                 }
6934                 if (!rdata || !rdata.success) {
6935                     Roo.log(rdata);
6936                     Roo.MessageBox.alert(Roo.encode(rdata));
6937                     return;
6938                 }
6939                 var data = rdata.data;
6940                 
6941                 if (this.uploadComplete) {
6942                    Roo.MessageBox.hide();
6943                    return;
6944                 }
6945                    
6946                 if (data){
6947                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6948                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6949                     );
6950                 }
6951                 this.uploadProgress.defer(2000,this);
6952             },
6953        
6954             failure: function(data) {
6955                 Roo.log('progress url failed ');
6956                 Roo.log(data);
6957             },
6958             scope : this
6959         });
6960            
6961     },
6962     
6963     
6964     run : function()
6965     {
6966         // run get Values on the form, so it syncs any secondary forms.
6967         this.form.getValues();
6968         
6969         var o = this.options;
6970         var method = this.getMethod();
6971         var isPost = method == 'POST';
6972         if(o.clientValidation === false || this.form.isValid()){
6973             
6974             if (this.form.progressUrl) {
6975                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6976                     (new Date() * 1) + '' + Math.random());
6977                     
6978             } 
6979             
6980             
6981             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6982                 form:this.form.el.dom,
6983                 url:this.getUrl(!isPost),
6984                 method: method,
6985                 params:isPost ? this.getParams() : null,
6986                 isUpload: this.form.fileUpload
6987             }));
6988             
6989             this.uploadProgress();
6990
6991         }else if (o.clientValidation !== false){ // client validation failed
6992             this.failureType = Roo.form.Action.CLIENT_INVALID;
6993             this.form.afterAction(this, false);
6994         }
6995     },
6996
6997     success : function(response)
6998     {
6999         this.uploadComplete= true;
7000         if (this.haveProgress) {
7001             Roo.MessageBox.hide();
7002         }
7003         
7004         
7005         var result = this.processResponse(response);
7006         if(result === true || result.success){
7007             this.form.afterAction(this, true);
7008             return;
7009         }
7010         if(result.errors){
7011             this.form.markInvalid(result.errors);
7012             this.failureType = Roo.form.Action.SERVER_INVALID;
7013         }
7014         this.form.afterAction(this, false);
7015     },
7016     failure : function(response)
7017     {
7018         this.uploadComplete= true;
7019         if (this.haveProgress) {
7020             Roo.MessageBox.hide();
7021         }
7022         
7023         this.response = response;
7024         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7025         this.form.afterAction(this, false);
7026     },
7027     
7028     handleResponse : function(response){
7029         if(this.form.errorReader){
7030             var rs = this.form.errorReader.read(response);
7031             var errors = [];
7032             if(rs.records){
7033                 for(var i = 0, len = rs.records.length; i < len; i++) {
7034                     var r = rs.records[i];
7035                     errors[i] = r.data;
7036                 }
7037             }
7038             if(errors.length < 1){
7039                 errors = null;
7040             }
7041             return {
7042                 success : rs.success,
7043                 errors : errors
7044             };
7045         }
7046         var ret = false;
7047         try {
7048             ret = Roo.decode(response.responseText);
7049         } catch (e) {
7050             ret = {
7051                 success: false,
7052                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7053                 errors : []
7054             };
7055         }
7056         return ret;
7057         
7058     }
7059 });
7060
7061
7062 Roo.form.Action.Load = function(form, options){
7063     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7064     this.reader = this.form.reader;
7065 };
7066
7067 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7068     type : 'load',
7069
7070     run : function(){
7071         
7072         Roo.Ajax.request(Roo.apply(
7073                 this.createCallback(), {
7074                     method:this.getMethod(),
7075                     url:this.getUrl(false),
7076                     params:this.getParams()
7077         }));
7078     },
7079
7080     success : function(response){
7081         
7082         var result = this.processResponse(response);
7083         if(result === true || !result.success || !result.data){
7084             this.failureType = Roo.form.Action.LOAD_FAILURE;
7085             this.form.afterAction(this, false);
7086             return;
7087         }
7088         this.form.clearInvalid();
7089         this.form.setValues(result.data);
7090         this.form.afterAction(this, true);
7091     },
7092
7093     handleResponse : function(response){
7094         if(this.form.reader){
7095             var rs = this.form.reader.read(response);
7096             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7097             return {
7098                 success : rs.success,
7099                 data : data
7100             };
7101         }
7102         return Roo.decode(response.responseText);
7103     }
7104 });
7105
7106 Roo.form.Action.ACTION_TYPES = {
7107     'load' : Roo.form.Action.Load,
7108     'submit' : Roo.form.Action.Submit
7109 };/*
7110  * - LGPL
7111  *
7112  * form
7113  * 
7114  */
7115
7116 /**
7117  * @class Roo.bootstrap.Form
7118  * @extends Roo.bootstrap.Component
7119  * Bootstrap Form class
7120  * @cfg {String} method  GET | POST (default POST)
7121  * @cfg {String} labelAlign top | left (default top)
7122  * @cfg {String} align left  | right - for navbars
7123  * @cfg {Boolean} loadMask load mask when submit (default true)
7124
7125  * 
7126  * @constructor
7127  * Create a new Form
7128  * @param {Object} config The config object
7129  */
7130
7131
7132 Roo.bootstrap.Form = function(config){
7133     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7134     this.addEvents({
7135         /**
7136          * @event clientvalidation
7137          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7138          * @param {Form} this
7139          * @param {Boolean} valid true if the form has passed client-side validation
7140          */
7141         clientvalidation: true,
7142         /**
7143          * @event beforeaction
7144          * Fires before any action is performed. Return false to cancel the action.
7145          * @param {Form} this
7146          * @param {Action} action The action to be performed
7147          */
7148         beforeaction: true,
7149         /**
7150          * @event actionfailed
7151          * Fires when an action fails.
7152          * @param {Form} this
7153          * @param {Action} action The action that failed
7154          */
7155         actionfailed : true,
7156         /**
7157          * @event actioncomplete
7158          * Fires when an action is completed.
7159          * @param {Form} this
7160          * @param {Action} action The action that completed
7161          */
7162         actioncomplete : true
7163     });
7164     
7165 };
7166
7167 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7168       
7169      /**
7170      * @cfg {String} method
7171      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7172      */
7173     method : 'POST',
7174     /**
7175      * @cfg {String} url
7176      * The URL to use for form actions if one isn't supplied in the action options.
7177      */
7178     /**
7179      * @cfg {Boolean} fileUpload
7180      * Set to true if this form is a file upload.
7181      */
7182      
7183     /**
7184      * @cfg {Object} baseParams
7185      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7186      */
7187       
7188     /**
7189      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7190      */
7191     timeout: 30,
7192     /**
7193      * @cfg {Sting} align (left|right) for navbar forms
7194      */
7195     align : 'left',
7196
7197     // private
7198     activeAction : null,
7199  
7200     /**
7201      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7202      * element by passing it or its id or mask the form itself by passing in true.
7203      * @type Mixed
7204      */
7205     waitMsgTarget : false,
7206     
7207     loadMask : true,
7208     
7209     getAutoCreate : function(){
7210         
7211         var cfg = {
7212             tag: 'form',
7213             method : this.method || 'POST',
7214             id : this.id || Roo.id(),
7215             cls : ''
7216         };
7217         if (this.parent().xtype.match(/^Nav/)) {
7218             cfg.cls = 'navbar-form navbar-' + this.align;
7219             
7220         }
7221         
7222         if (this.labelAlign == 'left' ) {
7223             cfg.cls += ' form-horizontal';
7224         }
7225         
7226         
7227         return cfg;
7228     },
7229     initEvents : function()
7230     {
7231         this.el.on('submit', this.onSubmit, this);
7232         // this was added as random key presses on the form where triggering form submit.
7233         this.el.on('keypress', function(e) {
7234             if (e.getCharCode() != 13) {
7235                 return true;
7236             }
7237             // we might need to allow it for textareas.. and some other items.
7238             // check e.getTarget().
7239             
7240             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7241                 return true;
7242             }
7243         
7244             Roo.log("keypress blocked");
7245             
7246             e.preventDefault();
7247             return false;
7248         });
7249         
7250     },
7251     // private
7252     onSubmit : function(e){
7253         e.stopEvent();
7254     },
7255     
7256      /**
7257      * Returns true if client-side validation on the form is successful.
7258      * @return Boolean
7259      */
7260     isValid : function(){
7261         var items = this.getItems();
7262         var valid = true;
7263         items.each(function(f){
7264            if(!f.validate()){
7265                valid = false;
7266                
7267            }
7268         });
7269         return valid;
7270     },
7271     /**
7272      * Returns true if any fields in this form have changed since their original load.
7273      * @return Boolean
7274      */
7275     isDirty : function(){
7276         var dirty = false;
7277         var items = this.getItems();
7278         items.each(function(f){
7279            if(f.isDirty()){
7280                dirty = true;
7281                return false;
7282            }
7283            return true;
7284         });
7285         return dirty;
7286     },
7287      /**
7288      * Performs a predefined action (submit or load) or custom actions you define on this form.
7289      * @param {String} actionName The name of the action type
7290      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7291      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7292      * accept other config options):
7293      * <pre>
7294 Property          Type             Description
7295 ----------------  ---------------  ----------------------------------------------------------------------------------
7296 url               String           The url for the action (defaults to the form's url)
7297 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7298 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7299 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7300                                    validate the form on the client (defaults to false)
7301      * </pre>
7302      * @return {BasicForm} this
7303      */
7304     doAction : function(action, options){
7305         if(typeof action == 'string'){
7306             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7307         }
7308         if(this.fireEvent('beforeaction', this, action) !== false){
7309             this.beforeAction(action);
7310             action.run.defer(100, action);
7311         }
7312         return this;
7313     },
7314     
7315     // private
7316     beforeAction : function(action){
7317         var o = action.options;
7318         
7319         if(this.loadMask){
7320             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7321         }
7322         // not really supported yet.. ??
7323         
7324         //if(this.waitMsgTarget === true){
7325         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7326         //}else if(this.waitMsgTarget){
7327         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7328         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7329         //}else {
7330         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7331        // }
7332          
7333     },
7334
7335     // private
7336     afterAction : function(action, success){
7337         this.activeAction = null;
7338         var o = action.options;
7339         
7340         //if(this.waitMsgTarget === true){
7341             this.el.unmask();
7342         //}else if(this.waitMsgTarget){
7343         //    this.waitMsgTarget.unmask();
7344         //}else{
7345         //    Roo.MessageBox.updateProgress(1);
7346         //    Roo.MessageBox.hide();
7347        // }
7348         // 
7349         if(success){
7350             if(o.reset){
7351                 this.reset();
7352             }
7353             Roo.callback(o.success, o.scope, [this, action]);
7354             this.fireEvent('actioncomplete', this, action);
7355             
7356         }else{
7357             
7358             // failure condition..
7359             // we have a scenario where updates need confirming.
7360             // eg. if a locking scenario exists..
7361             // we look for { errors : { needs_confirm : true }} in the response.
7362             if (
7363                 (typeof(action.result) != 'undefined')  &&
7364                 (typeof(action.result.errors) != 'undefined')  &&
7365                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7366            ){
7367                 var _t = this;
7368                 Roo.log("not supported yet");
7369                  /*
7370                 
7371                 Roo.MessageBox.confirm(
7372                     "Change requires confirmation",
7373                     action.result.errorMsg,
7374                     function(r) {
7375                         if (r != 'yes') {
7376                             return;
7377                         }
7378                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7379                     }
7380                     
7381                 );
7382                 */
7383                 
7384                 
7385                 return;
7386             }
7387             
7388             Roo.callback(o.failure, o.scope, [this, action]);
7389             // show an error message if no failed handler is set..
7390             if (!this.hasListener('actionfailed')) {
7391                 Roo.log("need to add dialog support");
7392                 /*
7393                 Roo.MessageBox.alert("Error",
7394                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7395                         action.result.errorMsg :
7396                         "Saving Failed, please check your entries or try again"
7397                 );
7398                 */
7399             }
7400             
7401             this.fireEvent('actionfailed', this, action);
7402         }
7403         
7404     },
7405     /**
7406      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7407      * @param {String} id The value to search for
7408      * @return Field
7409      */
7410     findField : function(id){
7411         var items = this.getItems();
7412         var field = items.get(id);
7413         if(!field){
7414              items.each(function(f){
7415                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7416                     field = f;
7417                     return false;
7418                 }
7419                 return true;
7420             });
7421         }
7422         return field || null;
7423     },
7424      /**
7425      * Mark fields in this form invalid in bulk.
7426      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7427      * @return {BasicForm} this
7428      */
7429     markInvalid : function(errors){
7430         if(errors instanceof Array){
7431             for(var i = 0, len = errors.length; i < len; i++){
7432                 var fieldError = errors[i];
7433                 var f = this.findField(fieldError.id);
7434                 if(f){
7435                     f.markInvalid(fieldError.msg);
7436                 }
7437             }
7438         }else{
7439             var field, id;
7440             for(id in errors){
7441                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7442                     field.markInvalid(errors[id]);
7443                 }
7444             }
7445         }
7446         //Roo.each(this.childForms || [], function (f) {
7447         //    f.markInvalid(errors);
7448         //});
7449         
7450         return this;
7451     },
7452
7453     /**
7454      * Set values for fields in this form in bulk.
7455      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7456      * @return {BasicForm} this
7457      */
7458     setValues : function(values){
7459         if(values instanceof Array){ // array of objects
7460             for(var i = 0, len = values.length; i < len; i++){
7461                 var v = values[i];
7462                 var f = this.findField(v.id);
7463                 if(f){
7464                     f.setValue(v.value);
7465                     if(this.trackResetOnLoad){
7466                         f.originalValue = f.getValue();
7467                     }
7468                 }
7469             }
7470         }else{ // object hash
7471             var field, id;
7472             for(id in values){
7473                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7474                     
7475                     if (field.setFromData && 
7476                         field.valueField && 
7477                         field.displayField &&
7478                         // combos' with local stores can 
7479                         // be queried via setValue()
7480                         // to set their value..
7481                         (field.store && !field.store.isLocal)
7482                         ) {
7483                         // it's a combo
7484                         var sd = { };
7485                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7486                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7487                         field.setFromData(sd);
7488                         
7489                     } else {
7490                         field.setValue(values[id]);
7491                     }
7492                     
7493                     
7494                     if(this.trackResetOnLoad){
7495                         field.originalValue = field.getValue();
7496                     }
7497                 }
7498             }
7499         }
7500          
7501         //Roo.each(this.childForms || [], function (f) {
7502         //    f.setValues(values);
7503         //});
7504                 
7505         return this;
7506     },
7507
7508     /**
7509      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7510      * they are returned as an array.
7511      * @param {Boolean} asString
7512      * @return {Object}
7513      */
7514     getValues : function(asString){
7515         //if (this.childForms) {
7516             // copy values from the child forms
7517         //    Roo.each(this.childForms, function (f) {
7518         //        this.setValues(f.getValues());
7519         //    }, this);
7520         //}
7521         
7522         
7523         
7524         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7525         if(asString === true){
7526             return fs;
7527         }
7528         return Roo.urlDecode(fs);
7529     },
7530     
7531     /**
7532      * Returns the fields in this form as an object with key/value pairs. 
7533      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7534      * @return {Object}
7535      */
7536     getFieldValues : function(with_hidden)
7537     {
7538         var items = this.getItems();
7539         var ret = {};
7540         items.each(function(f){
7541             if (!f.getName()) {
7542                 return;
7543             }
7544             var v = f.getValue();
7545             if (f.inputType =='radio') {
7546                 if (typeof(ret[f.getName()]) == 'undefined') {
7547                     ret[f.getName()] = ''; // empty..
7548                 }
7549                 
7550                 if (!f.el.dom.checked) {
7551                     return;
7552                     
7553                 }
7554                 v = f.el.dom.value;
7555                 
7556             }
7557             
7558             // not sure if this supported any more..
7559             if ((typeof(v) == 'object') && f.getRawValue) {
7560                 v = f.getRawValue() ; // dates..
7561             }
7562             // combo boxes where name != hiddenName...
7563             if (f.name != f.getName()) {
7564                 ret[f.name] = f.getRawValue();
7565             }
7566             ret[f.getName()] = v;
7567         });
7568         
7569         return ret;
7570     },
7571
7572     /**
7573      * Clears all invalid messages in this form.
7574      * @return {BasicForm} this
7575      */
7576     clearInvalid : function(){
7577         var items = this.getItems();
7578         
7579         items.each(function(f){
7580            f.clearInvalid();
7581         });
7582         
7583         
7584         
7585         return this;
7586     },
7587
7588     /**
7589      * Resets this form.
7590      * @return {BasicForm} this
7591      */
7592     reset : function(){
7593         var items = this.getItems();
7594         items.each(function(f){
7595             f.reset();
7596         });
7597         
7598         Roo.each(this.childForms || [], function (f) {
7599             f.reset();
7600         });
7601        
7602         
7603         return this;
7604     },
7605     getItems : function()
7606     {
7607         var r=new Roo.util.MixedCollection(false, function(o){
7608             return o.id || (o.id = Roo.id());
7609         });
7610         var iter = function(el) {
7611             if (el.inputEl) {
7612                 r.add(el);
7613             }
7614             if (!el.items) {
7615                 return;
7616             }
7617             Roo.each(el.items,function(e) {
7618                 iter(e);
7619             });
7620             
7621             
7622         };
7623         
7624         iter(this);
7625         return r;
7626         
7627         
7628         
7629         
7630     }
7631     
7632 });
7633
7634  
7635 /*
7636  * Based on:
7637  * Ext JS Library 1.1.1
7638  * Copyright(c) 2006-2007, Ext JS, LLC.
7639  *
7640  * Originally Released Under LGPL - original licence link has changed is not relivant.
7641  *
7642  * Fork - LGPL
7643  * <script type="text/javascript">
7644  */
7645 /**
7646  * @class Roo.form.VTypes
7647  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7648  * @singleton
7649  */
7650 Roo.form.VTypes = function(){
7651     // closure these in so they are only created once.
7652     var alpha = /^[a-zA-Z_]+$/;
7653     var alphanum = /^[a-zA-Z0-9_]+$/;
7654     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7655     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7656
7657     // All these messages and functions are configurable
7658     return {
7659         /**
7660          * The function used to validate email addresses
7661          * @param {String} value The email address
7662          */
7663         'email' : function(v){
7664             return email.test(v);
7665         },
7666         /**
7667          * The error text to display when the email validation function returns false
7668          * @type String
7669          */
7670         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7671         /**
7672          * The keystroke filter mask to be applied on email input
7673          * @type RegExp
7674          */
7675         'emailMask' : /[a-z0-9_\.\-@]/i,
7676
7677         /**
7678          * The function used to validate URLs
7679          * @param {String} value The URL
7680          */
7681         'url' : function(v){
7682             return url.test(v);
7683         },
7684         /**
7685          * The error text to display when the url validation function returns false
7686          * @type String
7687          */
7688         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7689         
7690         /**
7691          * The function used to validate alpha values
7692          * @param {String} value The value
7693          */
7694         'alpha' : function(v){
7695             return alpha.test(v);
7696         },
7697         /**
7698          * The error text to display when the alpha validation function returns false
7699          * @type String
7700          */
7701         'alphaText' : 'This field should only contain letters and _',
7702         /**
7703          * The keystroke filter mask to be applied on alpha input
7704          * @type RegExp
7705          */
7706         'alphaMask' : /[a-z_]/i,
7707
7708         /**
7709          * The function used to validate alphanumeric values
7710          * @param {String} value The value
7711          */
7712         'alphanum' : function(v){
7713             return alphanum.test(v);
7714         },
7715         /**
7716          * The error text to display when the alphanumeric validation function returns false
7717          * @type String
7718          */
7719         'alphanumText' : 'This field should only contain letters, numbers and _',
7720         /**
7721          * The keystroke filter mask to be applied on alphanumeric input
7722          * @type RegExp
7723          */
7724         'alphanumMask' : /[a-z0-9_]/i
7725     };
7726 }();/*
7727  * - LGPL
7728  *
7729  * Input
7730  * 
7731  */
7732
7733 /**
7734  * @class Roo.bootstrap.Input
7735  * @extends Roo.bootstrap.Component
7736  * Bootstrap Input class
7737  * @cfg {Boolean} disabled is it disabled
7738  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7739  * @cfg {String} name name of the input
7740  * @cfg {string} fieldLabel - the label associated
7741  * @cfg {string} placeholder - placeholder to put in text.
7742  * @cfg {string}  before - input group add on before
7743  * @cfg {string} after - input group add on after
7744  * @cfg {string} size - (lg|sm) or leave empty..
7745  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7746  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7747  * @cfg {Number} md colspan out of 12 for computer-sized screens
7748  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7749  * @cfg {string} value default value of the input
7750  * @cfg {Number} labelWidth set the width of label (0-12)
7751  * @cfg {String} labelAlign (top|left)
7752  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7753  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7754
7755  * @cfg {String} align (left|center|right) Default left
7756  * @cfg {Boolean} forceFeedback (true|false) Default false
7757  * 
7758  * 
7759  * 
7760  * 
7761  * @constructor
7762  * Create a new Input
7763  * @param {Object} config The config object
7764  */
7765
7766 Roo.bootstrap.Input = function(config){
7767     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7768    
7769         this.addEvents({
7770             /**
7771              * @event focus
7772              * Fires when this field receives input focus.
7773              * @param {Roo.form.Field} this
7774              */
7775             focus : true,
7776             /**
7777              * @event blur
7778              * Fires when this field loses input focus.
7779              * @param {Roo.form.Field} this
7780              */
7781             blur : true,
7782             /**
7783              * @event specialkey
7784              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7785              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7786              * @param {Roo.form.Field} this
7787              * @param {Roo.EventObject} e The event object
7788              */
7789             specialkey : true,
7790             /**
7791              * @event change
7792              * Fires just before the field blurs if the field value has changed.
7793              * @param {Roo.form.Field} this
7794              * @param {Mixed} newValue The new value
7795              * @param {Mixed} oldValue The original value
7796              */
7797             change : true,
7798             /**
7799              * @event invalid
7800              * Fires after the field has been marked as invalid.
7801              * @param {Roo.form.Field} this
7802              * @param {String} msg The validation message
7803              */
7804             invalid : true,
7805             /**
7806              * @event valid
7807              * Fires after the field has been validated with no errors.
7808              * @param {Roo.form.Field} this
7809              */
7810             valid : true,
7811              /**
7812              * @event keyup
7813              * Fires after the key up
7814              * @param {Roo.form.Field} this
7815              * @param {Roo.EventObject}  e The event Object
7816              */
7817             keyup : true
7818         });
7819 };
7820
7821 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7822      /**
7823      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7824       automatic validation (defaults to "keyup").
7825      */
7826     validationEvent : "keyup",
7827      /**
7828      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7829      */
7830     validateOnBlur : true,
7831     /**
7832      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7833      */
7834     validationDelay : 250,
7835      /**
7836      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7837      */
7838     focusClass : "x-form-focus",  // not needed???
7839     
7840        
7841     /**
7842      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7843      */
7844     invalidClass : "has-warning",
7845     
7846     /**
7847      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7848      */
7849     validClass : "has-success",
7850     
7851     /**
7852      * @cfg {Boolean} hasFeedback (true|false) default true
7853      */
7854     hasFeedback : true,
7855     
7856     /**
7857      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7858      */
7859     invalidFeedbackClass : "glyphicon-warning-sign",
7860     
7861     /**
7862      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7863      */
7864     validFeedbackClass : "glyphicon-ok",
7865     
7866     /**
7867      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7868      */
7869     selectOnFocus : false,
7870     
7871      /**
7872      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7873      */
7874     maskRe : null,
7875        /**
7876      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7877      */
7878     vtype : null,
7879     
7880       /**
7881      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7882      */
7883     disableKeyFilter : false,
7884     
7885        /**
7886      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7887      */
7888     disabled : false,
7889      /**
7890      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7891      */
7892     allowBlank : true,
7893     /**
7894      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7895      */
7896     blankText : "This field is required",
7897     
7898      /**
7899      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7900      */
7901     minLength : 0,
7902     /**
7903      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7904      */
7905     maxLength : Number.MAX_VALUE,
7906     /**
7907      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7908      */
7909     minLengthText : "The minimum length for this field is {0}",
7910     /**
7911      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7912      */
7913     maxLengthText : "The maximum length for this field is {0}",
7914   
7915     
7916     /**
7917      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7918      * If available, this function will be called only after the basic validators all return true, and will be passed the
7919      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7920      */
7921     validator : null,
7922     /**
7923      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7924      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7925      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7926      */
7927     regex : null,
7928     /**
7929      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7930      */
7931     regexText : "",
7932     
7933     autocomplete: false,
7934     
7935     
7936     fieldLabel : '',
7937     inputType : 'text',
7938     
7939     name : false,
7940     placeholder: false,
7941     before : false,
7942     after : false,
7943     size : false,
7944     hasFocus : false,
7945     preventMark: false,
7946     isFormField : true,
7947     value : '',
7948     labelWidth : 2,
7949     labelAlign : false,
7950     readOnly : false,
7951     align : false,
7952     formatedValue : false,
7953     forceFeedback : false,
7954     
7955     parentLabelAlign : function()
7956     {
7957         var parent = this;
7958         while (parent.parent()) {
7959             parent = parent.parent();
7960             if (typeof(parent.labelAlign) !='undefined') {
7961                 return parent.labelAlign;
7962             }
7963         }
7964         return 'left';
7965         
7966     },
7967     
7968     getAutoCreate : function(){
7969         
7970         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7971         
7972         var id = Roo.id();
7973         
7974         var cfg = {};
7975         
7976         if(this.inputType != 'hidden'){
7977             cfg.cls = 'form-group' //input-group
7978         }
7979         
7980         var input =  {
7981             tag: 'input',
7982             id : id,
7983             type : this.inputType,
7984             value : this.value,
7985             cls : 'form-control',
7986             placeholder : this.placeholder || '',
7987             autocomplete : this.autocomplete || 'new-password'
7988         };
7989         
7990         
7991         if(this.align){
7992             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7993         }
7994         
7995         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7996             input.maxLength = this.maxLength;
7997         }
7998         
7999         if (this.disabled) {
8000             input.disabled=true;
8001         }
8002         
8003         if (this.readOnly) {
8004             input.readonly=true;
8005         }
8006         
8007         if (this.name) {
8008             input.name = this.name;
8009         }
8010         if (this.size) {
8011             input.cls += ' input-' + this.size;
8012         }
8013         var settings=this;
8014         ['xs','sm','md','lg'].map(function(size){
8015             if (settings[size]) {
8016                 cfg.cls += ' col-' + size + '-' + settings[size];
8017             }
8018         });
8019         
8020         var inputblock = input;
8021         
8022         var feedback = {
8023             tag: 'span',
8024             cls: 'glyphicon form-control-feedback'
8025         };
8026             
8027         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8028             
8029             inputblock = {
8030                 cls : 'has-feedback',
8031                 cn :  [
8032                     input,
8033                     feedback
8034                 ] 
8035             };  
8036         }
8037         
8038         if (this.before || this.after) {
8039             
8040             inputblock = {
8041                 cls : 'input-group',
8042                 cn :  [] 
8043             };
8044             
8045             if (this.before && typeof(this.before) == 'string') {
8046                 
8047                 inputblock.cn.push({
8048                     tag :'span',
8049                     cls : 'roo-input-before input-group-addon',
8050                     html : this.before
8051                 });
8052             }
8053             if (this.before && typeof(this.before) == 'object') {
8054                 this.before = Roo.factory(this.before);
8055                 
8056                 inputblock.cn.push({
8057                     tag :'span',
8058                     cls : 'roo-input-before input-group-' +
8059                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8060                 });
8061             }
8062             
8063             inputblock.cn.push(input);
8064             
8065             if (this.after && typeof(this.after) == 'string') {
8066                 inputblock.cn.push({
8067                     tag :'span',
8068                     cls : 'roo-input-after input-group-addon',
8069                     html : this.after
8070                 });
8071             }
8072             if (this.after && typeof(this.after) == 'object') {
8073                 this.after = Roo.factory(this.after);
8074                 
8075                 inputblock.cn.push({
8076                     tag :'span',
8077                     cls : 'roo-input-after input-group-' +
8078                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8079                 });
8080             }
8081             
8082             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8083                 inputblock.cls += ' has-feedback';
8084                 inputblock.cn.push(feedback);
8085             }
8086         };
8087         
8088         if (align ==='left' && this.fieldLabel.length) {
8089                 
8090                 cfg.cn = [
8091                     
8092                     {
8093                         tag: 'label',
8094                         'for' :  id,
8095                         cls : 'control-label col-sm-' + this.labelWidth,
8096                         html : this.fieldLabel
8097                         
8098                     },
8099                     {
8100                         cls : "col-sm-" + (12 - this.labelWidth), 
8101                         cn: [
8102                             inputblock
8103                         ]
8104                     }
8105                     
8106                 ];
8107         } else if ( this.fieldLabel.length) {
8108                 
8109                  cfg.cn = [
8110                    
8111                     {
8112                         tag: 'label',
8113                         //cls : 'input-group-addon',
8114                         html : this.fieldLabel
8115                         
8116                     },
8117                     
8118                     inputblock
8119                     
8120                 ];
8121
8122         } else {
8123             
8124                 cfg.cn = [
8125                     
8126                         inputblock
8127                     
8128                 ];
8129                 
8130                 
8131         };
8132         
8133         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8134            cfg.cls += ' navbar-form';
8135         }
8136         
8137         return cfg;
8138         
8139     },
8140     /**
8141      * return the real input element.
8142      */
8143     inputEl: function ()
8144     {
8145         return this.el.select('input.form-control',true).first();
8146     },
8147     
8148     tooltipEl : function()
8149     {
8150         return this.inputEl();
8151     },
8152     
8153     setDisabled : function(v)
8154     {
8155         var i  = this.inputEl().dom;
8156         if (!v) {
8157             i.removeAttribute('disabled');
8158             return;
8159             
8160         }
8161         i.setAttribute('disabled','true');
8162     },
8163     initEvents : function()
8164     {
8165           
8166         this.inputEl().on("keydown" , this.fireKey,  this);
8167         this.inputEl().on("focus", this.onFocus,  this);
8168         this.inputEl().on("blur", this.onBlur,  this);
8169         
8170         this.inputEl().relayEvent('keyup', this);
8171  
8172         // reference to original value for reset
8173         this.originalValue = this.getValue();
8174         //Roo.form.TextField.superclass.initEvents.call(this);
8175         if(this.validationEvent == 'keyup'){
8176             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8177             this.inputEl().on('keyup', this.filterValidation, this);
8178         }
8179         else if(this.validationEvent !== false){
8180             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8181         }
8182         
8183         if(this.selectOnFocus){
8184             this.on("focus", this.preFocus, this);
8185             
8186         }
8187         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8188             this.inputEl().on("keypress", this.filterKeys, this);
8189         }
8190        /* if(this.grow){
8191             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8192             this.el.on("click", this.autoSize,  this);
8193         }
8194         */
8195         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8196             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8197         }
8198         
8199         if (typeof(this.before) == 'object') {
8200             this.before.render(this.el.select('.roo-input-before',true).first());
8201         }
8202         if (typeof(this.after) == 'object') {
8203             this.after.render(this.el.select('.roo-input-after',true).first());
8204         }
8205         
8206         
8207     },
8208     filterValidation : function(e){
8209         if(!e.isNavKeyPress()){
8210             this.validationTask.delay(this.validationDelay);
8211         }
8212     },
8213      /**
8214      * Validates the field value
8215      * @return {Boolean} True if the value is valid, else false
8216      */
8217     validate : function(){
8218         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8219         if(this.disabled || this.validateValue(this.getRawValue())){
8220             this.markValid();
8221             return true;
8222         }
8223         
8224         this.markInvalid();
8225         return false;
8226     },
8227     
8228     
8229     /**
8230      * Validates a value according to the field's validation rules and marks the field as invalid
8231      * if the validation fails
8232      * @param {Mixed} value The value to validate
8233      * @return {Boolean} True if the value is valid, else false
8234      */
8235     validateValue : function(value){
8236         if(value.length < 1)  { // if it's blank
8237             if(this.allowBlank){
8238                 return true;
8239             }
8240             return false;
8241         }
8242         
8243         if(value.length < this.minLength){
8244             return false;
8245         }
8246         if(value.length > this.maxLength){
8247             return false;
8248         }
8249         if(this.vtype){
8250             var vt = Roo.form.VTypes;
8251             if(!vt[this.vtype](value, this)){
8252                 return false;
8253             }
8254         }
8255         if(typeof this.validator == "function"){
8256             var msg = this.validator(value);
8257             if(msg !== true){
8258                 return false;
8259             }
8260         }
8261         
8262         if(this.regex && !this.regex.test(value)){
8263             return false;
8264         }
8265         
8266         return true;
8267     },
8268
8269     
8270     
8271      // private
8272     fireKey : function(e){
8273         //Roo.log('field ' + e.getKey());
8274         if(e.isNavKeyPress()){
8275             this.fireEvent("specialkey", this, e);
8276         }
8277     },
8278     focus : function (selectText){
8279         if(this.rendered){
8280             this.inputEl().focus();
8281             if(selectText === true){
8282                 this.inputEl().dom.select();
8283             }
8284         }
8285         return this;
8286     } ,
8287     
8288     onFocus : function(){
8289         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8290            // this.el.addClass(this.focusClass);
8291         }
8292         if(!this.hasFocus){
8293             this.hasFocus = true;
8294             this.startValue = this.getValue();
8295             this.fireEvent("focus", this);
8296         }
8297     },
8298     
8299     beforeBlur : Roo.emptyFn,
8300
8301     
8302     // private
8303     onBlur : function(){
8304         this.beforeBlur();
8305         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8306             //this.el.removeClass(this.focusClass);
8307         }
8308         this.hasFocus = false;
8309         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8310             this.validate();
8311         }
8312         var v = this.getValue();
8313         if(String(v) !== String(this.startValue)){
8314             this.fireEvent('change', this, v, this.startValue);
8315         }
8316         this.fireEvent("blur", this);
8317     },
8318     
8319     /**
8320      * Resets the current field value to the originally loaded value and clears any validation messages
8321      */
8322     reset : function(){
8323         this.setValue(this.originalValue);
8324         this.validate();
8325     },
8326      /**
8327      * Returns the name of the field
8328      * @return {Mixed} name The name field
8329      */
8330     getName: function(){
8331         return this.name;
8332     },
8333      /**
8334      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8335      * @return {Mixed} value The field value
8336      */
8337     getValue : function(){
8338         
8339         var v = this.inputEl().getValue();
8340         
8341         return v;
8342     },
8343     /**
8344      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8345      * @return {Mixed} value The field value
8346      */
8347     getRawValue : function(){
8348         var v = this.inputEl().getValue();
8349         
8350         return v;
8351     },
8352     
8353     /**
8354      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8355      * @param {Mixed} value The value to set
8356      */
8357     setRawValue : function(v){
8358         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8359     },
8360     
8361     selectText : function(start, end){
8362         var v = this.getRawValue();
8363         if(v.length > 0){
8364             start = start === undefined ? 0 : start;
8365             end = end === undefined ? v.length : end;
8366             var d = this.inputEl().dom;
8367             if(d.setSelectionRange){
8368                 d.setSelectionRange(start, end);
8369             }else if(d.createTextRange){
8370                 var range = d.createTextRange();
8371                 range.moveStart("character", start);
8372                 range.moveEnd("character", v.length-end);
8373                 range.select();
8374             }
8375         }
8376     },
8377     
8378     /**
8379      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8380      * @param {Mixed} value The value to set
8381      */
8382     setValue : function(v){
8383         this.value = v;
8384         if(this.rendered){
8385             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8386             this.validate();
8387         }
8388     },
8389     
8390     /*
8391     processValue : function(value){
8392         if(this.stripCharsRe){
8393             var newValue = value.replace(this.stripCharsRe, '');
8394             if(newValue !== value){
8395                 this.setRawValue(newValue);
8396                 return newValue;
8397             }
8398         }
8399         return value;
8400     },
8401   */
8402     preFocus : function(){
8403         
8404         if(this.selectOnFocus){
8405             this.inputEl().dom.select();
8406         }
8407     },
8408     filterKeys : function(e){
8409         var k = e.getKey();
8410         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8411             return;
8412         }
8413         var c = e.getCharCode(), cc = String.fromCharCode(c);
8414         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8415             return;
8416         }
8417         if(!this.maskRe.test(cc)){
8418             e.stopEvent();
8419         }
8420     },
8421      /**
8422      * Clear any invalid styles/messages for this field
8423      */
8424     clearInvalid : function(){
8425         
8426         if(!this.el || this.preventMark){ // not rendered
8427             return;
8428         }
8429         
8430         var label = this.el.select('label', true).first();
8431         var icon = this.el.select('i.fa-star', true).first();
8432         
8433         if(label && icon){
8434             icon.remove();
8435         }
8436         
8437         this.el.removeClass(this.invalidClass);
8438         
8439         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8440             
8441             var feedback = this.el.select('.form-control-feedback', true).first();
8442             
8443             if(feedback){
8444                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8445             }
8446             
8447         }
8448         
8449         this.fireEvent('valid', this);
8450     },
8451     
8452      /**
8453      * Mark this field as valid
8454      */
8455     markValid : function()
8456     {
8457         if(!this.el  || this.preventMark){ // not rendered
8458             return;
8459         }
8460         
8461         this.el.removeClass([this.invalidClass, this.validClass]);
8462         
8463         var feedback = this.el.select('.form-control-feedback', true).first();
8464             
8465         if(feedback){
8466             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8467         }
8468
8469         if(this.disabled || this.allowBlank){
8470             return;
8471         }
8472         
8473         var formGroup = this.el.findParent('.form-group', false, true);
8474         
8475         if(formGroup){
8476             
8477             var label = formGroup.select('label', true).first();
8478             var icon = formGroup.select('i.fa-star', true).first();
8479             
8480             if(label && icon){
8481                 icon.remove();
8482             }
8483         }
8484         
8485         this.el.addClass(this.validClass);
8486         
8487         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8488             
8489             var feedback = this.el.select('.form-control-feedback', true).first();
8490             
8491             if(feedback){
8492                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8493                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8494             }
8495             
8496         }
8497         
8498         this.fireEvent('valid', this);
8499     },
8500     
8501      /**
8502      * Mark this field as invalid
8503      * @param {String} msg The validation message
8504      */
8505     markInvalid : function(msg)
8506     {
8507         if(!this.el  || this.preventMark){ // not rendered
8508             return;
8509         }
8510         
8511         this.el.removeClass([this.invalidClass, this.validClass]);
8512         
8513         var feedback = this.el.select('.form-control-feedback', true).first();
8514             
8515         if(feedback){
8516             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8517         }
8518
8519         if(this.disabled || this.allowBlank){
8520             return;
8521         }
8522         
8523         var formGroup = this.el.findParent('.form-group', false, true);
8524         
8525         if(formGroup){
8526             var label = formGroup.select('label', true).first();
8527             var icon = formGroup.select('i.fa-star', true).first();
8528
8529             if(!this.getValue().length && label && !icon){
8530                 this.el.findParent('.form-group', false, true).createChild({
8531                     tag : 'i',
8532                     cls : 'text-danger fa fa-lg fa-star',
8533                     tooltip : 'This field is required',
8534                     style : 'margin-right:5px;'
8535                 }, label, true);
8536             }
8537         }
8538         
8539         
8540         this.el.addClass(this.invalidClass);
8541         
8542         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8543             
8544             var feedback = this.el.select('.form-control-feedback', true).first();
8545             
8546             if(feedback){
8547                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8548                 
8549                 if(this.getValue().length || this.forceFeedback){
8550                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8551                 }
8552                 
8553             }
8554             
8555         }
8556         
8557         this.fireEvent('invalid', this, msg);
8558     },
8559     // private
8560     SafariOnKeyDown : function(event)
8561     {
8562         // this is a workaround for a password hang bug on chrome/ webkit.
8563         
8564         var isSelectAll = false;
8565         
8566         if(this.inputEl().dom.selectionEnd > 0){
8567             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8568         }
8569         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8570             event.preventDefault();
8571             this.setValue('');
8572             return;
8573         }
8574         
8575         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8576             
8577             event.preventDefault();
8578             // this is very hacky as keydown always get's upper case.
8579             //
8580             var cc = String.fromCharCode(event.getCharCode());
8581             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8582             
8583         }
8584     },
8585     adjustWidth : function(tag, w){
8586         tag = tag.toLowerCase();
8587         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8588             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8589                 if(tag == 'input'){
8590                     return w + 2;
8591                 }
8592                 if(tag == 'textarea'){
8593                     return w-2;
8594                 }
8595             }else if(Roo.isOpera){
8596                 if(tag == 'input'){
8597                     return w + 2;
8598                 }
8599                 if(tag == 'textarea'){
8600                     return w-2;
8601                 }
8602             }
8603         }
8604         return w;
8605     }
8606     
8607 });
8608
8609  
8610 /*
8611  * - LGPL
8612  *
8613  * Input
8614  * 
8615  */
8616
8617 /**
8618  * @class Roo.bootstrap.TextArea
8619  * @extends Roo.bootstrap.Input
8620  * Bootstrap TextArea class
8621  * @cfg {Number} cols Specifies the visible width of a text area
8622  * @cfg {Number} rows Specifies the visible number of lines in a text area
8623  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8624  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8625  * @cfg {string} html text
8626  * 
8627  * @constructor
8628  * Create a new TextArea
8629  * @param {Object} config The config object
8630  */
8631
8632 Roo.bootstrap.TextArea = function(config){
8633     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8634    
8635 };
8636
8637 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8638      
8639     cols : false,
8640     rows : 5,
8641     readOnly : false,
8642     warp : 'soft',
8643     resize : false,
8644     value: false,
8645     html: false,
8646     
8647     getAutoCreate : function(){
8648         
8649         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8650         
8651         var id = Roo.id();
8652         
8653         var cfg = {};
8654         
8655         var input =  {
8656             tag: 'textarea',
8657             id : id,
8658             warp : this.warp,
8659             rows : this.rows,
8660             value : this.value || '',
8661             html: this.html || '',
8662             cls : 'form-control',
8663             placeholder : this.placeholder || '' 
8664             
8665         };
8666         
8667         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8668             input.maxLength = this.maxLength;
8669         }
8670         
8671         if(this.resize){
8672             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8673         }
8674         
8675         if(this.cols){
8676             input.cols = this.cols;
8677         }
8678         
8679         if (this.readOnly) {
8680             input.readonly = true;
8681         }
8682         
8683         if (this.name) {
8684             input.name = this.name;
8685         }
8686         
8687         if (this.size) {
8688             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8689         }
8690         
8691         var settings=this;
8692         ['xs','sm','md','lg'].map(function(size){
8693             if (settings[size]) {
8694                 cfg.cls += ' col-' + size + '-' + settings[size];
8695             }
8696         });
8697         
8698         var inputblock = input;
8699         
8700         if(this.hasFeedback && !this.allowBlank){
8701             
8702             var feedback = {
8703                 tag: 'span',
8704                 cls: 'glyphicon form-control-feedback'
8705             };
8706
8707             inputblock = {
8708                 cls : 'has-feedback',
8709                 cn :  [
8710                     input,
8711                     feedback
8712                 ] 
8713             };  
8714         }
8715         
8716         
8717         if (this.before || this.after) {
8718             
8719             inputblock = {
8720                 cls : 'input-group',
8721                 cn :  [] 
8722             };
8723             if (this.before) {
8724                 inputblock.cn.push({
8725                     tag :'span',
8726                     cls : 'input-group-addon',
8727                     html : this.before
8728                 });
8729             }
8730             
8731             inputblock.cn.push(input);
8732             
8733             if(this.hasFeedback && !this.allowBlank){
8734                 inputblock.cls += ' has-feedback';
8735                 inputblock.cn.push(feedback);
8736             }
8737             
8738             if (this.after) {
8739                 inputblock.cn.push({
8740                     tag :'span',
8741                     cls : 'input-group-addon',
8742                     html : this.after
8743                 });
8744             }
8745             
8746         }
8747         
8748         if (align ==='left' && this.fieldLabel.length) {
8749 //                Roo.log("left and has label");
8750                 cfg.cn = [
8751                     
8752                     {
8753                         tag: 'label',
8754                         'for' :  id,
8755                         cls : 'control-label col-sm-' + this.labelWidth,
8756                         html : this.fieldLabel
8757                         
8758                     },
8759                     {
8760                         cls : "col-sm-" + (12 - this.labelWidth), 
8761                         cn: [
8762                             inputblock
8763                         ]
8764                     }
8765                     
8766                 ];
8767         } else if ( this.fieldLabel.length) {
8768 //                Roo.log(" label");
8769                  cfg.cn = [
8770                    
8771                     {
8772                         tag: 'label',
8773                         //cls : 'input-group-addon',
8774                         html : this.fieldLabel
8775                         
8776                     },
8777                     
8778                     inputblock
8779                     
8780                 ];
8781
8782         } else {
8783             
8784 //                   Roo.log(" no label && no align");
8785                 cfg.cn = [
8786                     
8787                         inputblock
8788                     
8789                 ];
8790                 
8791                 
8792         }
8793         
8794         if (this.disabled) {
8795             input.disabled=true;
8796         }
8797         
8798         return cfg;
8799         
8800     },
8801     /**
8802      * return the real textarea element.
8803      */
8804     inputEl: function ()
8805     {
8806         return this.el.select('textarea.form-control',true).first();
8807     },
8808     
8809     /**
8810      * Clear any invalid styles/messages for this field
8811      */
8812     clearInvalid : function()
8813     {
8814         
8815         if(!this.el || this.preventMark){ // not rendered
8816             return;
8817         }
8818         
8819         var label = this.el.select('label', true).first();
8820         var icon = this.el.select('i.fa-star', true).first();
8821         
8822         if(label && icon){
8823             icon.remove();
8824         }
8825         
8826         this.el.removeClass(this.invalidClass);
8827         
8828         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8829             
8830             var feedback = this.el.select('.form-control-feedback', true).first();
8831             
8832             if(feedback){
8833                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8834             }
8835             
8836         }
8837         
8838         this.fireEvent('valid', this);
8839     },
8840     
8841      /**
8842      * Mark this field as valid
8843      */
8844     markValid : function()
8845     {
8846         if(!this.el  || this.preventMark){ // not rendered
8847             return;
8848         }
8849         
8850         this.el.removeClass([this.invalidClass, this.validClass]);
8851         
8852         var feedback = this.el.select('.form-control-feedback', true).first();
8853             
8854         if(feedback){
8855             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8856         }
8857
8858         if(this.disabled || this.allowBlank){
8859             return;
8860         }
8861         
8862         var label = this.el.select('label', true).first();
8863         var icon = this.el.select('i.fa-star', true).first();
8864         
8865         if(label && icon){
8866             icon.remove();
8867         }
8868         
8869         this.el.addClass(this.validClass);
8870         
8871         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8872             
8873             var feedback = this.el.select('.form-control-feedback', true).first();
8874             
8875             if(feedback){
8876                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8877                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8878             }
8879             
8880         }
8881         
8882         this.fireEvent('valid', this);
8883     },
8884     
8885      /**
8886      * Mark this field as invalid
8887      * @param {String} msg The validation message
8888      */
8889     markInvalid : function(msg)
8890     {
8891         if(!this.el  || this.preventMark){ // not rendered
8892             return;
8893         }
8894         
8895         this.el.removeClass([this.invalidClass, this.validClass]);
8896         
8897         var feedback = this.el.select('.form-control-feedback', true).first();
8898             
8899         if(feedback){
8900             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8901         }
8902
8903         if(this.disabled || this.allowBlank){
8904             return;
8905         }
8906         
8907         var label = this.el.select('label', true).first();
8908         var icon = this.el.select('i.fa-star', true).first();
8909         
8910         if(!this.getValue().length && label && !icon){
8911             this.el.createChild({
8912                 tag : 'i',
8913                 cls : 'text-danger fa fa-lg fa-star',
8914                 tooltip : 'This field is required',
8915                 style : 'margin-right:5px;'
8916             }, label, true);
8917         }
8918
8919         this.el.addClass(this.invalidClass);
8920         
8921         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8922             
8923             var feedback = this.el.select('.form-control-feedback', true).first();
8924             
8925             if(feedback){
8926                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8927                 
8928                 if(this.getValue().length || this.forceFeedback){
8929                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8930                 }
8931                 
8932             }
8933             
8934         }
8935         
8936         this.fireEvent('invalid', this, msg);
8937     }
8938 });
8939
8940  
8941 /*
8942  * - LGPL
8943  *
8944  * trigger field - base class for combo..
8945  * 
8946  */
8947  
8948 /**
8949  * @class Roo.bootstrap.TriggerField
8950  * @extends Roo.bootstrap.Input
8951  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8952  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8953  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8954  * for which you can provide a custom implementation.  For example:
8955  * <pre><code>
8956 var trigger = new Roo.bootstrap.TriggerField();
8957 trigger.onTriggerClick = myTriggerFn;
8958 trigger.applyTo('my-field');
8959 </code></pre>
8960  *
8961  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8962  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8963  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8964  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8965  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8966
8967  * @constructor
8968  * Create a new TriggerField.
8969  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8970  * to the base TextField)
8971  */
8972 Roo.bootstrap.TriggerField = function(config){
8973     this.mimicing = false;
8974     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8975 };
8976
8977 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8978     /**
8979      * @cfg {String} triggerClass A CSS class to apply to the trigger
8980      */
8981      /**
8982      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8983      */
8984     hideTrigger:false,
8985
8986     /**
8987      * @cfg {Boolean} removable (true|false) special filter default false
8988      */
8989     removable : false,
8990     
8991     /** @cfg {Boolean} grow @hide */
8992     /** @cfg {Number} growMin @hide */
8993     /** @cfg {Number} growMax @hide */
8994
8995     /**
8996      * @hide 
8997      * @method
8998      */
8999     autoSize: Roo.emptyFn,
9000     // private
9001     monitorTab : true,
9002     // private
9003     deferHeight : true,
9004
9005     
9006     actionMode : 'wrap',
9007     
9008     caret : false,
9009     
9010     
9011     getAutoCreate : function(){
9012        
9013         var align = this.labelAlign || this.parentLabelAlign();
9014         
9015         var id = Roo.id();
9016         
9017         var cfg = {
9018             cls: 'form-group' //input-group
9019         };
9020         
9021         
9022         var input =  {
9023             tag: 'input',
9024             id : id,
9025             type : this.inputType,
9026             cls : 'form-control',
9027             autocomplete: 'new-password',
9028             placeholder : this.placeholder || '' 
9029             
9030         };
9031         if (this.name) {
9032             input.name = this.name;
9033         }
9034         if (this.size) {
9035             input.cls += ' input-' + this.size;
9036         }
9037         
9038         if (this.disabled) {
9039             input.disabled=true;
9040         }
9041         
9042         var inputblock = input;
9043         
9044         if(this.hasFeedback && !this.allowBlank){
9045             
9046             var feedback = {
9047                 tag: 'span',
9048                 cls: 'glyphicon form-control-feedback'
9049             };
9050             
9051             if(this.removable && !this.editable && !this.tickable){
9052                 inputblock = {
9053                     cls : 'has-feedback',
9054                     cn :  [
9055                         inputblock,
9056                         {
9057                             tag: 'button',
9058                             html : 'x',
9059                             cls : 'roo-combo-removable-btn close'
9060                         },
9061                         feedback
9062                     ] 
9063                 };
9064             } else {
9065                 inputblock = {
9066                     cls : 'has-feedback',
9067                     cn :  [
9068                         inputblock,
9069                         feedback
9070                     ] 
9071                 };
9072             }
9073
9074         } else {
9075             if(this.removable && !this.editable && !this.tickable){
9076                 inputblock = {
9077                     cls : 'roo-removable',
9078                     cn :  [
9079                         inputblock,
9080                         {
9081                             tag: 'button',
9082                             html : 'x',
9083                             cls : 'roo-combo-removable-btn close'
9084                         }
9085                     ] 
9086                 };
9087             }
9088         }
9089         
9090         if (this.before || this.after) {
9091             
9092             inputblock = {
9093                 cls : 'input-group',
9094                 cn :  [] 
9095             };
9096             if (this.before) {
9097                 inputblock.cn.push({
9098                     tag :'span',
9099                     cls : 'input-group-addon',
9100                     html : this.before
9101                 });
9102             }
9103             
9104             inputblock.cn.push(input);
9105             
9106             if(this.hasFeedback && !this.allowBlank){
9107                 inputblock.cls += ' has-feedback';
9108                 inputblock.cn.push(feedback);
9109             }
9110             
9111             if (this.after) {
9112                 inputblock.cn.push({
9113                     tag :'span',
9114                     cls : 'input-group-addon',
9115                     html : this.after
9116                 });
9117             }
9118             
9119         };
9120         
9121         var box = {
9122             tag: 'div',
9123             cn: [
9124                 {
9125                     tag: 'input',
9126                     type : 'hidden',
9127                     cls: 'form-hidden-field'
9128                 },
9129                 inputblock
9130             ]
9131             
9132         };
9133         
9134         if(this.multiple){
9135             box = {
9136                 tag: 'div',
9137                 cn: [
9138                     {
9139                         tag: 'input',
9140                         type : 'hidden',
9141                         cls: 'form-hidden-field'
9142                     },
9143                     {
9144                         tag: 'ul',
9145                         cls: 'select2-choices',
9146                         cn:[
9147                             {
9148                                 tag: 'li',
9149                                 cls: 'select2-search-field',
9150                                 cn: [
9151
9152                                     inputblock
9153                                 ]
9154                             }
9155                         ]
9156                     }
9157                 ]
9158             }
9159         };
9160         
9161         var combobox = {
9162             cls: 'select2-container input-group',
9163             cn: [
9164                 box
9165 //                {
9166 //                    tag: 'ul',
9167 //                    cls: 'typeahead typeahead-long dropdown-menu',
9168 //                    style: 'display:none'
9169 //                }
9170             ]
9171         };
9172         
9173         if(!this.multiple && this.showToggleBtn){
9174             
9175             var caret = {
9176                         tag: 'span',
9177                         cls: 'caret'
9178              };
9179             if (this.caret != false) {
9180                 caret = {
9181                      tag: 'i',
9182                      cls: 'fa fa-' + this.caret
9183                 };
9184                 
9185             }
9186             
9187             combobox.cn.push({
9188                 tag :'span',
9189                 cls : 'input-group-addon btn dropdown-toggle',
9190                 cn : [
9191                     caret,
9192                     {
9193                         tag: 'span',
9194                         cls: 'combobox-clear',
9195                         cn  : [
9196                             {
9197                                 tag : 'i',
9198                                 cls: 'icon-remove'
9199                             }
9200                         ]
9201                     }
9202                 ]
9203
9204             })
9205         }
9206         
9207         if(this.multiple){
9208             combobox.cls += ' select2-container-multi';
9209         }
9210         
9211         if (align ==='left' && this.fieldLabel.length) {
9212             
9213 //                Roo.log("left and has label");
9214                 cfg.cn = [
9215                     
9216                     {
9217                         tag: 'label',
9218                         'for' :  id,
9219                         cls : 'control-label col-sm-' + this.labelWidth,
9220                         html : this.fieldLabel
9221                         
9222                     },
9223                     {
9224                         cls : "col-sm-" + (12 - this.labelWidth), 
9225                         cn: [
9226                             combobox
9227                         ]
9228                     }
9229                     
9230                 ];
9231         } else if ( this.fieldLabel.length) {
9232 //                Roo.log(" label");
9233                  cfg.cn = [
9234                    
9235                     {
9236                         tag: 'label',
9237                         //cls : 'input-group-addon',
9238                         html : this.fieldLabel
9239                         
9240                     },
9241                     
9242                     combobox
9243                     
9244                 ];
9245
9246         } else {
9247             
9248 //                Roo.log(" no label && no align");
9249                 cfg = combobox
9250                      
9251                 
9252         }
9253          
9254         var settings=this;
9255         ['xs','sm','md','lg'].map(function(size){
9256             if (settings[size]) {
9257                 cfg.cls += ' col-' + size + '-' + settings[size];
9258             }
9259         });
9260         
9261         return cfg;
9262         
9263     },
9264     
9265     
9266     
9267     // private
9268     onResize : function(w, h){
9269 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9270 //        if(typeof w == 'number'){
9271 //            var x = w - this.trigger.getWidth();
9272 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9273 //            this.trigger.setStyle('left', x+'px');
9274 //        }
9275     },
9276
9277     // private
9278     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9279
9280     // private
9281     getResizeEl : function(){
9282         return this.inputEl();
9283     },
9284
9285     // private
9286     getPositionEl : function(){
9287         return this.inputEl();
9288     },
9289
9290     // private
9291     alignErrorIcon : function(){
9292         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9293     },
9294
9295     // private
9296     initEvents : function(){
9297         
9298         this.createList();
9299         
9300         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9301         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9302         if(!this.multiple && this.showToggleBtn){
9303             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9304             if(this.hideTrigger){
9305                 this.trigger.setDisplayed(false);
9306             }
9307             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9308         }
9309         
9310         if(this.multiple){
9311             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9312         }
9313         
9314         if(this.removable && !this.editable && !this.tickable){
9315             var close = this.closeTriggerEl();
9316             
9317             if(close){
9318                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9319                 close.on('click', this.removeBtnClick, this, close);
9320             }
9321         }
9322         
9323         //this.trigger.addClassOnOver('x-form-trigger-over');
9324         //this.trigger.addClassOnClick('x-form-trigger-click');
9325         
9326         //if(!this.width){
9327         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9328         //}
9329     },
9330     
9331     closeTriggerEl : function()
9332     {
9333         var close = this.el.select('.roo-combo-removable-btn', true).first();
9334         return close ? close : false;
9335     },
9336     
9337     removeBtnClick : function(e, h, el)
9338     {
9339         e.preventDefault();
9340         
9341         if(this.fireEvent("remove", this) !== false){
9342             this.reset();
9343         }
9344     },
9345     
9346     createList : function()
9347     {
9348         this.list = Roo.get(document.body).createChild({
9349             tag: 'ul',
9350             cls: 'typeahead typeahead-long dropdown-menu',
9351             style: 'display:none'
9352         });
9353         
9354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9355         
9356     },
9357
9358     // private
9359     initTrigger : function(){
9360        
9361     },
9362
9363     // private
9364     onDestroy : function(){
9365         if(this.trigger){
9366             this.trigger.removeAllListeners();
9367           //  this.trigger.remove();
9368         }
9369         //if(this.wrap){
9370         //    this.wrap.remove();
9371         //}
9372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9373     },
9374
9375     // private
9376     onFocus : function(){
9377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9378         /*
9379         if(!this.mimicing){
9380             this.wrap.addClass('x-trigger-wrap-focus');
9381             this.mimicing = true;
9382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9383             if(this.monitorTab){
9384                 this.el.on("keydown", this.checkTab, this);
9385             }
9386         }
9387         */
9388     },
9389
9390     // private
9391     checkTab : function(e){
9392         if(e.getKey() == e.TAB){
9393             this.triggerBlur();
9394         }
9395     },
9396
9397     // private
9398     onBlur : function(){
9399         // do nothing
9400     },
9401
9402     // private
9403     mimicBlur : function(e, t){
9404         /*
9405         if(!this.wrap.contains(t) && this.validateBlur()){
9406             this.triggerBlur();
9407         }
9408         */
9409     },
9410
9411     // private
9412     triggerBlur : function(){
9413         this.mimicing = false;
9414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9415         if(this.monitorTab){
9416             this.el.un("keydown", this.checkTab, this);
9417         }
9418         //this.wrap.removeClass('x-trigger-wrap-focus');
9419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9420     },
9421
9422     // private
9423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9424     validateBlur : function(e, t){
9425         return true;
9426     },
9427
9428     // private
9429     onDisable : function(){
9430         this.inputEl().dom.disabled = true;
9431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9432         //if(this.wrap){
9433         //    this.wrap.addClass('x-item-disabled');
9434         //}
9435     },
9436
9437     // private
9438     onEnable : function(){
9439         this.inputEl().dom.disabled = false;
9440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9441         //if(this.wrap){
9442         //    this.el.removeClass('x-item-disabled');
9443         //}
9444     },
9445
9446     // private
9447     onShow : function(){
9448         var ae = this.getActionEl();
9449         
9450         if(ae){
9451             ae.dom.style.display = '';
9452             ae.dom.style.visibility = 'visible';
9453         }
9454     },
9455
9456     // private
9457     
9458     onHide : function(){
9459         var ae = this.getActionEl();
9460         ae.dom.style.display = 'none';
9461     },
9462
9463     /**
9464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9465      * by an implementing function.
9466      * @method
9467      * @param {EventObject} e
9468      */
9469     onTriggerClick : Roo.emptyFn
9470 });
9471  /*
9472  * Based on:
9473  * Ext JS Library 1.1.1
9474  * Copyright(c) 2006-2007, Ext JS, LLC.
9475  *
9476  * Originally Released Under LGPL - original licence link has changed is not relivant.
9477  *
9478  * Fork - LGPL
9479  * <script type="text/javascript">
9480  */
9481
9482
9483 /**
9484  * @class Roo.data.SortTypes
9485  * @singleton
9486  * Defines the default sorting (casting?) comparison functions used when sorting data.
9487  */
9488 Roo.data.SortTypes = {
9489     /**
9490      * Default sort that does nothing
9491      * @param {Mixed} s The value being converted
9492      * @return {Mixed} The comparison value
9493      */
9494     none : function(s){
9495         return s;
9496     },
9497     
9498     /**
9499      * The regular expression used to strip tags
9500      * @type {RegExp}
9501      * @property
9502      */
9503     stripTagsRE : /<\/?[^>]+>/gi,
9504     
9505     /**
9506      * Strips all HTML tags to sort on text only
9507      * @param {Mixed} s The value being converted
9508      * @return {String} The comparison value
9509      */
9510     asText : function(s){
9511         return String(s).replace(this.stripTagsRE, "");
9512     },
9513     
9514     /**
9515      * Strips all HTML tags to sort on text only - Case insensitive
9516      * @param {Mixed} s The value being converted
9517      * @return {String} The comparison value
9518      */
9519     asUCText : function(s){
9520         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9521     },
9522     
9523     /**
9524      * Case insensitive string
9525      * @param {Mixed} s The value being converted
9526      * @return {String} The comparison value
9527      */
9528     asUCString : function(s) {
9529         return String(s).toUpperCase();
9530     },
9531     
9532     /**
9533      * Date sorting
9534      * @param {Mixed} s The value being converted
9535      * @return {Number} The comparison value
9536      */
9537     asDate : function(s) {
9538         if(!s){
9539             return 0;
9540         }
9541         if(s instanceof Date){
9542             return s.getTime();
9543         }
9544         return Date.parse(String(s));
9545     },
9546     
9547     /**
9548      * Float sorting
9549      * @param {Mixed} s The value being converted
9550      * @return {Float} The comparison value
9551      */
9552     asFloat : function(s) {
9553         var val = parseFloat(String(s).replace(/,/g, ""));
9554         if(isNaN(val)) {
9555             val = 0;
9556         }
9557         return val;
9558     },
9559     
9560     /**
9561      * Integer sorting
9562      * @param {Mixed} s The value being converted
9563      * @return {Number} The comparison value
9564      */
9565     asInt : function(s) {
9566         var val = parseInt(String(s).replace(/,/g, ""));
9567         if(isNaN(val)) {
9568             val = 0;
9569         }
9570         return val;
9571     }
9572 };/*
9573  * Based on:
9574  * Ext JS Library 1.1.1
9575  * Copyright(c) 2006-2007, Ext JS, LLC.
9576  *
9577  * Originally Released Under LGPL - original licence link has changed is not relivant.
9578  *
9579  * Fork - LGPL
9580  * <script type="text/javascript">
9581  */
9582
9583 /**
9584 * @class Roo.data.Record
9585  * Instances of this class encapsulate both record <em>definition</em> information, and record
9586  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9587  * to access Records cached in an {@link Roo.data.Store} object.<br>
9588  * <p>
9589  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9590  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9591  * objects.<br>
9592  * <p>
9593  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9594  * @constructor
9595  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9596  * {@link #create}. The parameters are the same.
9597  * @param {Array} data An associative Array of data values keyed by the field name.
9598  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9599  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9600  * not specified an integer id is generated.
9601  */
9602 Roo.data.Record = function(data, id){
9603     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9604     this.data = data;
9605 };
9606
9607 /**
9608  * Generate a constructor for a specific record layout.
9609  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9610  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9611  * Each field definition object may contain the following properties: <ul>
9612  * <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,
9613  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9614  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9615  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9616  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9617  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9618  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9619  * this may be omitted.</p></li>
9620  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9621  * <ul><li>auto (Default, implies no conversion)</li>
9622  * <li>string</li>
9623  * <li>int</li>
9624  * <li>float</li>
9625  * <li>boolean</li>
9626  * <li>date</li></ul></p></li>
9627  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9628  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9629  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9630  * by the Reader into an object that will be stored in the Record. It is passed the
9631  * following parameters:<ul>
9632  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9633  * </ul></p></li>
9634  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9635  * </ul>
9636  * <br>usage:<br><pre><code>
9637 var TopicRecord = Roo.data.Record.create(
9638     {name: 'title', mapping: 'topic_title'},
9639     {name: 'author', mapping: 'username'},
9640     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9641     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9642     {name: 'lastPoster', mapping: 'user2'},
9643     {name: 'excerpt', mapping: 'post_text'}
9644 );
9645
9646 var myNewRecord = new TopicRecord({
9647     title: 'Do my job please',
9648     author: 'noobie',
9649     totalPosts: 1,
9650     lastPost: new Date(),
9651     lastPoster: 'Animal',
9652     excerpt: 'No way dude!'
9653 });
9654 myStore.add(myNewRecord);
9655 </code></pre>
9656  * @method create
9657  * @static
9658  */
9659 Roo.data.Record.create = function(o){
9660     var f = function(){
9661         f.superclass.constructor.apply(this, arguments);
9662     };
9663     Roo.extend(f, Roo.data.Record);
9664     var p = f.prototype;
9665     p.fields = new Roo.util.MixedCollection(false, function(field){
9666         return field.name;
9667     });
9668     for(var i = 0, len = o.length; i < len; i++){
9669         p.fields.add(new Roo.data.Field(o[i]));
9670     }
9671     f.getField = function(name){
9672         return p.fields.get(name);  
9673     };
9674     return f;
9675 };
9676
9677 Roo.data.Record.AUTO_ID = 1000;
9678 Roo.data.Record.EDIT = 'edit';
9679 Roo.data.Record.REJECT = 'reject';
9680 Roo.data.Record.COMMIT = 'commit';
9681
9682 Roo.data.Record.prototype = {
9683     /**
9684      * Readonly flag - true if this record has been modified.
9685      * @type Boolean
9686      */
9687     dirty : false,
9688     editing : false,
9689     error: null,
9690     modified: null,
9691
9692     // private
9693     join : function(store){
9694         this.store = store;
9695     },
9696
9697     /**
9698      * Set the named field to the specified value.
9699      * @param {String} name The name of the field to set.
9700      * @param {Object} value The value to set the field to.
9701      */
9702     set : function(name, value){
9703         if(this.data[name] == value){
9704             return;
9705         }
9706         this.dirty = true;
9707         if(!this.modified){
9708             this.modified = {};
9709         }
9710         if(typeof this.modified[name] == 'undefined'){
9711             this.modified[name] = this.data[name];
9712         }
9713         this.data[name] = value;
9714         if(!this.editing && this.store){
9715             this.store.afterEdit(this);
9716         }       
9717     },
9718
9719     /**
9720      * Get the value of the named field.
9721      * @param {String} name The name of the field to get the value of.
9722      * @return {Object} The value of the field.
9723      */
9724     get : function(name){
9725         return this.data[name]; 
9726     },
9727
9728     // private
9729     beginEdit : function(){
9730         this.editing = true;
9731         this.modified = {}; 
9732     },
9733
9734     // private
9735     cancelEdit : function(){
9736         this.editing = false;
9737         delete this.modified;
9738     },
9739
9740     // private
9741     endEdit : function(){
9742         this.editing = false;
9743         if(this.dirty && this.store){
9744             this.store.afterEdit(this);
9745         }
9746     },
9747
9748     /**
9749      * Usually called by the {@link Roo.data.Store} which owns the Record.
9750      * Rejects all changes made to the Record since either creation, or the last commit operation.
9751      * Modified fields are reverted to their original values.
9752      * <p>
9753      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9754      * of reject operations.
9755      */
9756     reject : function(){
9757         var m = this.modified;
9758         for(var n in m){
9759             if(typeof m[n] != "function"){
9760                 this.data[n] = m[n];
9761             }
9762         }
9763         this.dirty = false;
9764         delete this.modified;
9765         this.editing = false;
9766         if(this.store){
9767             this.store.afterReject(this);
9768         }
9769     },
9770
9771     /**
9772      * Usually called by the {@link Roo.data.Store} which owns the Record.
9773      * Commits all changes made to the Record since either creation, or the last commit operation.
9774      * <p>
9775      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9776      * of commit operations.
9777      */
9778     commit : function(){
9779         this.dirty = false;
9780         delete this.modified;
9781         this.editing = false;
9782         if(this.store){
9783             this.store.afterCommit(this);
9784         }
9785     },
9786
9787     // private
9788     hasError : function(){
9789         return this.error != null;
9790     },
9791
9792     // private
9793     clearError : function(){
9794         this.error = null;
9795     },
9796
9797     /**
9798      * Creates a copy of this record.
9799      * @param {String} id (optional) A new record id if you don't want to use this record's id
9800      * @return {Record}
9801      */
9802     copy : function(newId) {
9803         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9804     }
9805 };/*
9806  * Based on:
9807  * Ext JS Library 1.1.1
9808  * Copyright(c) 2006-2007, Ext JS, LLC.
9809  *
9810  * Originally Released Under LGPL - original licence link has changed is not relivant.
9811  *
9812  * Fork - LGPL
9813  * <script type="text/javascript">
9814  */
9815
9816
9817
9818 /**
9819  * @class Roo.data.Store
9820  * @extends Roo.util.Observable
9821  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9822  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9823  * <p>
9824  * 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
9825  * has no knowledge of the format of the data returned by the Proxy.<br>
9826  * <p>
9827  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9828  * instances from the data object. These records are cached and made available through accessor functions.
9829  * @constructor
9830  * Creates a new Store.
9831  * @param {Object} config A config object containing the objects needed for the Store to access data,
9832  * and read the data into Records.
9833  */
9834 Roo.data.Store = function(config){
9835     this.data = new Roo.util.MixedCollection(false);
9836     this.data.getKey = function(o){
9837         return o.id;
9838     };
9839     this.baseParams = {};
9840     // private
9841     this.paramNames = {
9842         "start" : "start",
9843         "limit" : "limit",
9844         "sort" : "sort",
9845         "dir" : "dir",
9846         "multisort" : "_multisort"
9847     };
9848
9849     if(config && config.data){
9850         this.inlineData = config.data;
9851         delete config.data;
9852     }
9853
9854     Roo.apply(this, config);
9855     
9856     if(this.reader){ // reader passed
9857         this.reader = Roo.factory(this.reader, Roo.data);
9858         this.reader.xmodule = this.xmodule || false;
9859         if(!this.recordType){
9860             this.recordType = this.reader.recordType;
9861         }
9862         if(this.reader.onMetaChange){
9863             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9864         }
9865     }
9866
9867     if(this.recordType){
9868         this.fields = this.recordType.prototype.fields;
9869     }
9870     this.modified = [];
9871
9872     this.addEvents({
9873         /**
9874          * @event datachanged
9875          * Fires when the data cache has changed, and a widget which is using this Store
9876          * as a Record cache should refresh its view.
9877          * @param {Store} this
9878          */
9879         datachanged : true,
9880         /**
9881          * @event metachange
9882          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9883          * @param {Store} this
9884          * @param {Object} meta The JSON metadata
9885          */
9886         metachange : true,
9887         /**
9888          * @event add
9889          * Fires when Records have been added to the Store
9890          * @param {Store} this
9891          * @param {Roo.data.Record[]} records The array of Records added
9892          * @param {Number} index The index at which the record(s) were added
9893          */
9894         add : true,
9895         /**
9896          * @event remove
9897          * Fires when a Record has been removed from the Store
9898          * @param {Store} this
9899          * @param {Roo.data.Record} record The Record that was removed
9900          * @param {Number} index The index at which the record was removed
9901          */
9902         remove : true,
9903         /**
9904          * @event update
9905          * Fires when a Record has been updated
9906          * @param {Store} this
9907          * @param {Roo.data.Record} record The Record that was updated
9908          * @param {String} operation The update operation being performed.  Value may be one of:
9909          * <pre><code>
9910  Roo.data.Record.EDIT
9911  Roo.data.Record.REJECT
9912  Roo.data.Record.COMMIT
9913          * </code></pre>
9914          */
9915         update : true,
9916         /**
9917          * @event clear
9918          * Fires when the data cache has been cleared.
9919          * @param {Store} this
9920          */
9921         clear : true,
9922         /**
9923          * @event beforeload
9924          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9925          * the load action will be canceled.
9926          * @param {Store} this
9927          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9928          */
9929         beforeload : true,
9930         /**
9931          * @event beforeloadadd
9932          * Fires after a new set of Records has been loaded.
9933          * @param {Store} this
9934          * @param {Roo.data.Record[]} records The Records that were loaded
9935          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9936          */
9937         beforeloadadd : true,
9938         /**
9939          * @event load
9940          * Fires after a new set of Records has been loaded, before they are added to the store.
9941          * @param {Store} this
9942          * @param {Roo.data.Record[]} records The Records that were loaded
9943          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9944          * @params {Object} return from reader
9945          */
9946         load : true,
9947         /**
9948          * @event loadexception
9949          * Fires if an exception occurs in the Proxy during loading.
9950          * Called with the signature of the Proxy's "loadexception" event.
9951          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9952          * 
9953          * @param {Proxy} 
9954          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9955          * @param {Object} load options 
9956          * @param {Object} jsonData from your request (normally this contains the Exception)
9957          */
9958         loadexception : true
9959     });
9960     
9961     if(this.proxy){
9962         this.proxy = Roo.factory(this.proxy, Roo.data);
9963         this.proxy.xmodule = this.xmodule || false;
9964         this.relayEvents(this.proxy,  ["loadexception"]);
9965     }
9966     this.sortToggle = {};
9967     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9968
9969     Roo.data.Store.superclass.constructor.call(this);
9970
9971     if(this.inlineData){
9972         this.loadData(this.inlineData);
9973         delete this.inlineData;
9974     }
9975 };
9976
9977 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9978      /**
9979     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9980     * without a remote query - used by combo/forms at present.
9981     */
9982     
9983     /**
9984     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9985     */
9986     /**
9987     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9988     */
9989     /**
9990     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9991     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9992     */
9993     /**
9994     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9995     * on any HTTP request
9996     */
9997     /**
9998     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9999     */
10000     /**
10001     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10002     */
10003     multiSort: false,
10004     /**
10005     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10006     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10007     */
10008     remoteSort : false,
10009
10010     /**
10011     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10012      * loaded or when a record is removed. (defaults to false).
10013     */
10014     pruneModifiedRecords : false,
10015
10016     // private
10017     lastOptions : null,
10018
10019     /**
10020      * Add Records to the Store and fires the add event.
10021      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10022      */
10023     add : function(records){
10024         records = [].concat(records);
10025         for(var i = 0, len = records.length; i < len; i++){
10026             records[i].join(this);
10027         }
10028         var index = this.data.length;
10029         this.data.addAll(records);
10030         this.fireEvent("add", this, records, index);
10031     },
10032
10033     /**
10034      * Remove a Record from the Store and fires the remove event.
10035      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10036      */
10037     remove : function(record){
10038         var index = this.data.indexOf(record);
10039         this.data.removeAt(index);
10040         if(this.pruneModifiedRecords){
10041             this.modified.remove(record);
10042         }
10043         this.fireEvent("remove", this, record, index);
10044     },
10045
10046     /**
10047      * Remove all Records from the Store and fires the clear event.
10048      */
10049     removeAll : function(){
10050         this.data.clear();
10051         if(this.pruneModifiedRecords){
10052             this.modified = [];
10053         }
10054         this.fireEvent("clear", this);
10055     },
10056
10057     /**
10058      * Inserts Records to the Store at the given index and fires the add event.
10059      * @param {Number} index The start index at which to insert the passed Records.
10060      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10061      */
10062     insert : function(index, records){
10063         records = [].concat(records);
10064         for(var i = 0, len = records.length; i < len; i++){
10065             this.data.insert(index, records[i]);
10066             records[i].join(this);
10067         }
10068         this.fireEvent("add", this, records, index);
10069     },
10070
10071     /**
10072      * Get the index within the cache of the passed Record.
10073      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10074      * @return {Number} The index of the passed Record. Returns -1 if not found.
10075      */
10076     indexOf : function(record){
10077         return this.data.indexOf(record);
10078     },
10079
10080     /**
10081      * Get the index within the cache of the Record with the passed id.
10082      * @param {String} id The id of the Record to find.
10083      * @return {Number} The index of the Record. Returns -1 if not found.
10084      */
10085     indexOfId : function(id){
10086         return this.data.indexOfKey(id);
10087     },
10088
10089     /**
10090      * Get the Record with the specified id.
10091      * @param {String} id The id of the Record to find.
10092      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10093      */
10094     getById : function(id){
10095         return this.data.key(id);
10096     },
10097
10098     /**
10099      * Get the Record at the specified index.
10100      * @param {Number} index The index of the Record to find.
10101      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10102      */
10103     getAt : function(index){
10104         return this.data.itemAt(index);
10105     },
10106
10107     /**
10108      * Returns a range of Records between specified indices.
10109      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10110      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10111      * @return {Roo.data.Record[]} An array of Records
10112      */
10113     getRange : function(start, end){
10114         return this.data.getRange(start, end);
10115     },
10116
10117     // private
10118     storeOptions : function(o){
10119         o = Roo.apply({}, o);
10120         delete o.callback;
10121         delete o.scope;
10122         this.lastOptions = o;
10123     },
10124
10125     /**
10126      * Loads the Record cache from the configured Proxy using the configured Reader.
10127      * <p>
10128      * If using remote paging, then the first load call must specify the <em>start</em>
10129      * and <em>limit</em> properties in the options.params property to establish the initial
10130      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10131      * <p>
10132      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10133      * and this call will return before the new data has been loaded. Perform any post-processing
10134      * in a callback function, or in a "load" event handler.</strong>
10135      * <p>
10136      * @param {Object} options An object containing properties which control loading options:<ul>
10137      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10138      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10139      * passed the following arguments:<ul>
10140      * <li>r : Roo.data.Record[]</li>
10141      * <li>options: Options object from the load call</li>
10142      * <li>success: Boolean success indicator</li></ul></li>
10143      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10144      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10145      * </ul>
10146      */
10147     load : function(options){
10148         options = options || {};
10149         if(this.fireEvent("beforeload", this, options) !== false){
10150             this.storeOptions(options);
10151             var p = Roo.apply(options.params || {}, this.baseParams);
10152             // if meta was not loaded from remote source.. try requesting it.
10153             if (!this.reader.metaFromRemote) {
10154                 p._requestMeta = 1;
10155             }
10156             if(this.sortInfo && this.remoteSort){
10157                 var pn = this.paramNames;
10158                 p[pn["sort"]] = this.sortInfo.field;
10159                 p[pn["dir"]] = this.sortInfo.direction;
10160             }
10161             if (this.multiSort) {
10162                 var pn = this.paramNames;
10163                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10164             }
10165             
10166             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10167         }
10168     },
10169
10170     /**
10171      * Reloads the Record cache from the configured Proxy using the configured Reader and
10172      * the options from the last load operation performed.
10173      * @param {Object} options (optional) An object containing properties which may override the options
10174      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10175      * the most recently used options are reused).
10176      */
10177     reload : function(options){
10178         this.load(Roo.applyIf(options||{}, this.lastOptions));
10179     },
10180
10181     // private
10182     // Called as a callback by the Reader during a load operation.
10183     loadRecords : function(o, options, success){
10184         if(!o || success === false){
10185             if(success !== false){
10186                 this.fireEvent("load", this, [], options, o);
10187             }
10188             if(options.callback){
10189                 options.callback.call(options.scope || this, [], options, false);
10190             }
10191             return;
10192         }
10193         // if data returned failure - throw an exception.
10194         if (o.success === false) {
10195             // show a message if no listener is registered.
10196             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10197                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10198             }
10199             // loadmask wil be hooked into this..
10200             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10201             return;
10202         }
10203         var r = o.records, t = o.totalRecords || r.length;
10204         
10205         this.fireEvent("beforeloadadd", this, r, options, o);
10206         
10207         if(!options || options.add !== true){
10208             if(this.pruneModifiedRecords){
10209                 this.modified = [];
10210             }
10211             for(var i = 0, len = r.length; i < len; i++){
10212                 r[i].join(this);
10213             }
10214             if(this.snapshot){
10215                 this.data = this.snapshot;
10216                 delete this.snapshot;
10217             }
10218             this.data.clear();
10219             this.data.addAll(r);
10220             this.totalLength = t;
10221             this.applySort();
10222             this.fireEvent("datachanged", this);
10223         }else{
10224             this.totalLength = Math.max(t, this.data.length+r.length);
10225             this.add(r);
10226         }
10227         this.fireEvent("load", this, r, options, o);
10228         if(options.callback){
10229             options.callback.call(options.scope || this, r, options, true);
10230         }
10231     },
10232
10233
10234     /**
10235      * Loads data from a passed data block. A Reader which understands the format of the data
10236      * must have been configured in the constructor.
10237      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10238      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10239      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10240      */
10241     loadData : function(o, append){
10242         var r = this.reader.readRecords(o);
10243         this.loadRecords(r, {add: append}, true);
10244     },
10245
10246     /**
10247      * Gets the number of cached records.
10248      * <p>
10249      * <em>If using paging, this may not be the total size of the dataset. If the data object
10250      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10251      * the data set size</em>
10252      */
10253     getCount : function(){
10254         return this.data.length || 0;
10255     },
10256
10257     /**
10258      * Gets the total number of records in the dataset as returned by the server.
10259      * <p>
10260      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10261      * the dataset size</em>
10262      */
10263     getTotalCount : function(){
10264         return this.totalLength || 0;
10265     },
10266
10267     /**
10268      * Returns the sort state of the Store as an object with two properties:
10269      * <pre><code>
10270  field {String} The name of the field by which the Records are sorted
10271  direction {String} The sort order, "ASC" or "DESC"
10272      * </code></pre>
10273      */
10274     getSortState : function(){
10275         return this.sortInfo;
10276     },
10277
10278     // private
10279     applySort : function(){
10280         if(this.sortInfo && !this.remoteSort){
10281             var s = this.sortInfo, f = s.field;
10282             var st = this.fields.get(f).sortType;
10283             var fn = function(r1, r2){
10284                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10285                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10286             };
10287             this.data.sort(s.direction, fn);
10288             if(this.snapshot && this.snapshot != this.data){
10289                 this.snapshot.sort(s.direction, fn);
10290             }
10291         }
10292     },
10293
10294     /**
10295      * Sets the default sort column and order to be used by the next load operation.
10296      * @param {String} fieldName The name of the field to sort by.
10297      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10298      */
10299     setDefaultSort : function(field, dir){
10300         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10301     },
10302
10303     /**
10304      * Sort the Records.
10305      * If remote sorting is used, the sort is performed on the server, and the cache is
10306      * reloaded. If local sorting is used, the cache is sorted internally.
10307      * @param {String} fieldName The name of the field to sort by.
10308      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10309      */
10310     sort : function(fieldName, dir){
10311         var f = this.fields.get(fieldName);
10312         if(!dir){
10313             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10314             
10315             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10316                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10317             }else{
10318                 dir = f.sortDir;
10319             }
10320         }
10321         this.sortToggle[f.name] = dir;
10322         this.sortInfo = {field: f.name, direction: dir};
10323         if(!this.remoteSort){
10324             this.applySort();
10325             this.fireEvent("datachanged", this);
10326         }else{
10327             this.load(this.lastOptions);
10328         }
10329     },
10330
10331     /**
10332      * Calls the specified function for each of the Records in the cache.
10333      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10334      * Returning <em>false</em> aborts and exits the iteration.
10335      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10336      */
10337     each : function(fn, scope){
10338         this.data.each(fn, scope);
10339     },
10340
10341     /**
10342      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10343      * (e.g., during paging).
10344      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10345      */
10346     getModifiedRecords : function(){
10347         return this.modified;
10348     },
10349
10350     // private
10351     createFilterFn : function(property, value, anyMatch){
10352         if(!value.exec){ // not a regex
10353             value = String(value);
10354             if(value.length == 0){
10355                 return false;
10356             }
10357             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10358         }
10359         return function(r){
10360             return value.test(r.data[property]);
10361         };
10362     },
10363
10364     /**
10365      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10366      * @param {String} property A field on your records
10367      * @param {Number} start The record index to start at (defaults to 0)
10368      * @param {Number} end The last record index to include (defaults to length - 1)
10369      * @return {Number} The sum
10370      */
10371     sum : function(property, start, end){
10372         var rs = this.data.items, v = 0;
10373         start = start || 0;
10374         end = (end || end === 0) ? end : rs.length-1;
10375
10376         for(var i = start; i <= end; i++){
10377             v += (rs[i].data[property] || 0);
10378         }
10379         return v;
10380     },
10381
10382     /**
10383      * Filter the records by a specified property.
10384      * @param {String} field A field on your records
10385      * @param {String/RegExp} value Either a string that the field
10386      * should start with or a RegExp to test against the field
10387      * @param {Boolean} anyMatch True to match any part not just the beginning
10388      */
10389     filter : function(property, value, anyMatch){
10390         var fn = this.createFilterFn(property, value, anyMatch);
10391         return fn ? this.filterBy(fn) : this.clearFilter();
10392     },
10393
10394     /**
10395      * Filter by a function. The specified function will be called with each
10396      * record in this data source. If the function returns true the record is included,
10397      * otherwise it is filtered.
10398      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10399      * @param {Object} scope (optional) The scope of the function (defaults to this)
10400      */
10401     filterBy : function(fn, scope){
10402         this.snapshot = this.snapshot || this.data;
10403         this.data = this.queryBy(fn, scope||this);
10404         this.fireEvent("datachanged", this);
10405     },
10406
10407     /**
10408      * Query the records by a specified property.
10409      * @param {String} field A field on your records
10410      * @param {String/RegExp} value Either a string that the field
10411      * should start with or a RegExp to test against the field
10412      * @param {Boolean} anyMatch True to match any part not just the beginning
10413      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10414      */
10415     query : function(property, value, anyMatch){
10416         var fn = this.createFilterFn(property, value, anyMatch);
10417         return fn ? this.queryBy(fn) : this.data.clone();
10418     },
10419
10420     /**
10421      * Query by a function. The specified function will be called with each
10422      * record in this data source. If the function returns true the record is included
10423      * in the results.
10424      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10425      * @param {Object} scope (optional) The scope of the function (defaults to this)
10426       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10427      **/
10428     queryBy : function(fn, scope){
10429         var data = this.snapshot || this.data;
10430         return data.filterBy(fn, scope||this);
10431     },
10432
10433     /**
10434      * Collects unique values for a particular dataIndex from this store.
10435      * @param {String} dataIndex The property to collect
10436      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10437      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10438      * @return {Array} An array of the unique values
10439      **/
10440     collect : function(dataIndex, allowNull, bypassFilter){
10441         var d = (bypassFilter === true && this.snapshot) ?
10442                 this.snapshot.items : this.data.items;
10443         var v, sv, r = [], l = {};
10444         for(var i = 0, len = d.length; i < len; i++){
10445             v = d[i].data[dataIndex];
10446             sv = String(v);
10447             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10448                 l[sv] = true;
10449                 r[r.length] = v;
10450             }
10451         }
10452         return r;
10453     },
10454
10455     /**
10456      * Revert to a view of the Record cache with no filtering applied.
10457      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10458      */
10459     clearFilter : function(suppressEvent){
10460         if(this.snapshot && this.snapshot != this.data){
10461             this.data = this.snapshot;
10462             delete this.snapshot;
10463             if(suppressEvent !== true){
10464                 this.fireEvent("datachanged", this);
10465             }
10466         }
10467     },
10468
10469     // private
10470     afterEdit : function(record){
10471         if(this.modified.indexOf(record) == -1){
10472             this.modified.push(record);
10473         }
10474         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10475     },
10476     
10477     // private
10478     afterReject : function(record){
10479         this.modified.remove(record);
10480         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10481     },
10482
10483     // private
10484     afterCommit : function(record){
10485         this.modified.remove(record);
10486         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10487     },
10488
10489     /**
10490      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10491      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10492      */
10493     commitChanges : function(){
10494         var m = this.modified.slice(0);
10495         this.modified = [];
10496         for(var i = 0, len = m.length; i < len; i++){
10497             m[i].commit();
10498         }
10499     },
10500
10501     /**
10502      * Cancel outstanding changes on all changed records.
10503      */
10504     rejectChanges : function(){
10505         var m = this.modified.slice(0);
10506         this.modified = [];
10507         for(var i = 0, len = m.length; i < len; i++){
10508             m[i].reject();
10509         }
10510     },
10511
10512     onMetaChange : function(meta, rtype, o){
10513         this.recordType = rtype;
10514         this.fields = rtype.prototype.fields;
10515         delete this.snapshot;
10516         this.sortInfo = meta.sortInfo || this.sortInfo;
10517         this.modified = [];
10518         this.fireEvent('metachange', this, this.reader.meta);
10519     },
10520     
10521     moveIndex : function(data, type)
10522     {
10523         var index = this.indexOf(data);
10524         
10525         var newIndex = index + type;
10526         
10527         this.remove(data);
10528         
10529         this.insert(newIndex, data);
10530         
10531     }
10532 });/*
10533  * Based on:
10534  * Ext JS Library 1.1.1
10535  * Copyright(c) 2006-2007, Ext JS, LLC.
10536  *
10537  * Originally Released Under LGPL - original licence link has changed is not relivant.
10538  *
10539  * Fork - LGPL
10540  * <script type="text/javascript">
10541  */
10542
10543 /**
10544  * @class Roo.data.SimpleStore
10545  * @extends Roo.data.Store
10546  * Small helper class to make creating Stores from Array data easier.
10547  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10548  * @cfg {Array} fields An array of field definition objects, or field name strings.
10549  * @cfg {Array} data The multi-dimensional array of data
10550  * @constructor
10551  * @param {Object} config
10552  */
10553 Roo.data.SimpleStore = function(config){
10554     Roo.data.SimpleStore.superclass.constructor.call(this, {
10555         isLocal : true,
10556         reader: new Roo.data.ArrayReader({
10557                 id: config.id
10558             },
10559             Roo.data.Record.create(config.fields)
10560         ),
10561         proxy : new Roo.data.MemoryProxy(config.data)
10562     });
10563     this.load();
10564 };
10565 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10566  * Based on:
10567  * Ext JS Library 1.1.1
10568  * Copyright(c) 2006-2007, Ext JS, LLC.
10569  *
10570  * Originally Released Under LGPL - original licence link has changed is not relivant.
10571  *
10572  * Fork - LGPL
10573  * <script type="text/javascript">
10574  */
10575
10576 /**
10577 /**
10578  * @extends Roo.data.Store
10579  * @class Roo.data.JsonStore
10580  * Small helper class to make creating Stores for JSON data easier. <br/>
10581 <pre><code>
10582 var store = new Roo.data.JsonStore({
10583     url: 'get-images.php',
10584     root: 'images',
10585     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10586 });
10587 </code></pre>
10588  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10589  * JsonReader and HttpProxy (unless inline data is provided).</b>
10590  * @cfg {Array} fields An array of field definition objects, or field name strings.
10591  * @constructor
10592  * @param {Object} config
10593  */
10594 Roo.data.JsonStore = function(c){
10595     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10596         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10597         reader: new Roo.data.JsonReader(c, c.fields)
10598     }));
10599 };
10600 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10601  * Based on:
10602  * Ext JS Library 1.1.1
10603  * Copyright(c) 2006-2007, Ext JS, LLC.
10604  *
10605  * Originally Released Under LGPL - original licence link has changed is not relivant.
10606  *
10607  * Fork - LGPL
10608  * <script type="text/javascript">
10609  */
10610
10611  
10612 Roo.data.Field = function(config){
10613     if(typeof config == "string"){
10614         config = {name: config};
10615     }
10616     Roo.apply(this, config);
10617     
10618     if(!this.type){
10619         this.type = "auto";
10620     }
10621     
10622     var st = Roo.data.SortTypes;
10623     // named sortTypes are supported, here we look them up
10624     if(typeof this.sortType == "string"){
10625         this.sortType = st[this.sortType];
10626     }
10627     
10628     // set default sortType for strings and dates
10629     if(!this.sortType){
10630         switch(this.type){
10631             case "string":
10632                 this.sortType = st.asUCString;
10633                 break;
10634             case "date":
10635                 this.sortType = st.asDate;
10636                 break;
10637             default:
10638                 this.sortType = st.none;
10639         }
10640     }
10641
10642     // define once
10643     var stripRe = /[\$,%]/g;
10644
10645     // prebuilt conversion function for this field, instead of
10646     // switching every time we're reading a value
10647     if(!this.convert){
10648         var cv, dateFormat = this.dateFormat;
10649         switch(this.type){
10650             case "":
10651             case "auto":
10652             case undefined:
10653                 cv = function(v){ return v; };
10654                 break;
10655             case "string":
10656                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10657                 break;
10658             case "int":
10659                 cv = function(v){
10660                     return v !== undefined && v !== null && v !== '' ?
10661                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10662                     };
10663                 break;
10664             case "float":
10665                 cv = function(v){
10666                     return v !== undefined && v !== null && v !== '' ?
10667                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10668                     };
10669                 break;
10670             case "bool":
10671             case "boolean":
10672                 cv = function(v){ return v === true || v === "true" || v == 1; };
10673                 break;
10674             case "date":
10675                 cv = function(v){
10676                     if(!v){
10677                         return '';
10678                     }
10679                     if(v instanceof Date){
10680                         return v;
10681                     }
10682                     if(dateFormat){
10683                         if(dateFormat == "timestamp"){
10684                             return new Date(v*1000);
10685                         }
10686                         return Date.parseDate(v, dateFormat);
10687                     }
10688                     var parsed = Date.parse(v);
10689                     return parsed ? new Date(parsed) : null;
10690                 };
10691              break;
10692             
10693         }
10694         this.convert = cv;
10695     }
10696 };
10697
10698 Roo.data.Field.prototype = {
10699     dateFormat: null,
10700     defaultValue: "",
10701     mapping: null,
10702     sortType : null,
10703     sortDir : "ASC"
10704 };/*
10705  * Based on:
10706  * Ext JS Library 1.1.1
10707  * Copyright(c) 2006-2007, Ext JS, LLC.
10708  *
10709  * Originally Released Under LGPL - original licence link has changed is not relivant.
10710  *
10711  * Fork - LGPL
10712  * <script type="text/javascript">
10713  */
10714  
10715 // Base class for reading structured data from a data source.  This class is intended to be
10716 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10717
10718 /**
10719  * @class Roo.data.DataReader
10720  * Base class for reading structured data from a data source.  This class is intended to be
10721  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10722  */
10723
10724 Roo.data.DataReader = function(meta, recordType){
10725     
10726     this.meta = meta;
10727     
10728     this.recordType = recordType instanceof Array ? 
10729         Roo.data.Record.create(recordType) : recordType;
10730 };
10731
10732 Roo.data.DataReader.prototype = {
10733      /**
10734      * Create an empty record
10735      * @param {Object} data (optional) - overlay some values
10736      * @return {Roo.data.Record} record created.
10737      */
10738     newRow :  function(d) {
10739         var da =  {};
10740         this.recordType.prototype.fields.each(function(c) {
10741             switch( c.type) {
10742                 case 'int' : da[c.name] = 0; break;
10743                 case 'date' : da[c.name] = new Date(); break;
10744                 case 'float' : da[c.name] = 0.0; break;
10745                 case 'boolean' : da[c.name] = false; break;
10746                 default : da[c.name] = ""; break;
10747             }
10748             
10749         });
10750         return new this.recordType(Roo.apply(da, d));
10751     }
10752     
10753 };/*
10754  * Based on:
10755  * Ext JS Library 1.1.1
10756  * Copyright(c) 2006-2007, Ext JS, LLC.
10757  *
10758  * Originally Released Under LGPL - original licence link has changed is not relivant.
10759  *
10760  * Fork - LGPL
10761  * <script type="text/javascript">
10762  */
10763
10764 /**
10765  * @class Roo.data.DataProxy
10766  * @extends Roo.data.Observable
10767  * This class is an abstract base class for implementations which provide retrieval of
10768  * unformatted data objects.<br>
10769  * <p>
10770  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10771  * (of the appropriate type which knows how to parse the data object) to provide a block of
10772  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10773  * <p>
10774  * Custom implementations must implement the load method as described in
10775  * {@link Roo.data.HttpProxy#load}.
10776  */
10777 Roo.data.DataProxy = function(){
10778     this.addEvents({
10779         /**
10780          * @event beforeload
10781          * Fires before a network request is made to retrieve a data object.
10782          * @param {Object} This DataProxy object.
10783          * @param {Object} params The params parameter to the load function.
10784          */
10785         beforeload : true,
10786         /**
10787          * @event load
10788          * Fires before the load method's callback is called.
10789          * @param {Object} This DataProxy object.
10790          * @param {Object} o The data object.
10791          * @param {Object} arg The callback argument object passed to the load function.
10792          */
10793         load : true,
10794         /**
10795          * @event loadexception
10796          * Fires if an Exception occurs during data retrieval.
10797          * @param {Object} This DataProxy object.
10798          * @param {Object} o The data object.
10799          * @param {Object} arg The callback argument object passed to the load function.
10800          * @param {Object} e The Exception.
10801          */
10802         loadexception : true
10803     });
10804     Roo.data.DataProxy.superclass.constructor.call(this);
10805 };
10806
10807 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10808
10809     /**
10810      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10811      */
10812 /*
10813  * Based on:
10814  * Ext JS Library 1.1.1
10815  * Copyright(c) 2006-2007, Ext JS, LLC.
10816  *
10817  * Originally Released Under LGPL - original licence link has changed is not relivant.
10818  *
10819  * Fork - LGPL
10820  * <script type="text/javascript">
10821  */
10822 /**
10823  * @class Roo.data.MemoryProxy
10824  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10825  * to the Reader when its load method is called.
10826  * @constructor
10827  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10828  */
10829 Roo.data.MemoryProxy = function(data){
10830     if (data.data) {
10831         data = data.data;
10832     }
10833     Roo.data.MemoryProxy.superclass.constructor.call(this);
10834     this.data = data;
10835 };
10836
10837 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10838     /**
10839      * Load data from the requested source (in this case an in-memory
10840      * data object passed to the constructor), read the data object into
10841      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10842      * process that block using the passed callback.
10843      * @param {Object} params This parameter is not used by the MemoryProxy class.
10844      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10845      * object into a block of Roo.data.Records.
10846      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10847      * The function must be passed <ul>
10848      * <li>The Record block object</li>
10849      * <li>The "arg" argument from the load function</li>
10850      * <li>A boolean success indicator</li>
10851      * </ul>
10852      * @param {Object} scope The scope in which to call the callback
10853      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10854      */
10855     load : function(params, reader, callback, scope, arg){
10856         params = params || {};
10857         var result;
10858         try {
10859             result = reader.readRecords(this.data);
10860         }catch(e){
10861             this.fireEvent("loadexception", this, arg, null, e);
10862             callback.call(scope, null, arg, false);
10863             return;
10864         }
10865         callback.call(scope, result, arg, true);
10866     },
10867     
10868     // private
10869     update : function(params, records){
10870         
10871     }
10872 });/*
10873  * Based on:
10874  * Ext JS Library 1.1.1
10875  * Copyright(c) 2006-2007, Ext JS, LLC.
10876  *
10877  * Originally Released Under LGPL - original licence link has changed is not relivant.
10878  *
10879  * Fork - LGPL
10880  * <script type="text/javascript">
10881  */
10882 /**
10883  * @class Roo.data.HttpProxy
10884  * @extends Roo.data.DataProxy
10885  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10886  * configured to reference a certain URL.<br><br>
10887  * <p>
10888  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10889  * from which the running page was served.<br><br>
10890  * <p>
10891  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10892  * <p>
10893  * Be aware that to enable the browser to parse an XML document, the server must set
10894  * the Content-Type header in the HTTP response to "text/xml".
10895  * @constructor
10896  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10897  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10898  * will be used to make the request.
10899  */
10900 Roo.data.HttpProxy = function(conn){
10901     Roo.data.HttpProxy.superclass.constructor.call(this);
10902     // is conn a conn config or a real conn?
10903     this.conn = conn;
10904     this.useAjax = !conn || !conn.events;
10905   
10906 };
10907
10908 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10909     // thse are take from connection...
10910     
10911     /**
10912      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10913      */
10914     /**
10915      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10916      * extra parameters to each request made by this object. (defaults to undefined)
10917      */
10918     /**
10919      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10920      *  to each request made by this object. (defaults to undefined)
10921      */
10922     /**
10923      * @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)
10924      */
10925     /**
10926      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10927      */
10928      /**
10929      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10930      * @type Boolean
10931      */
10932   
10933
10934     /**
10935      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10936      * @type Boolean
10937      */
10938     /**
10939      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10940      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10941      * a finer-grained basis than the DataProxy events.
10942      */
10943     getConnection : function(){
10944         return this.useAjax ? Roo.Ajax : this.conn;
10945     },
10946
10947     /**
10948      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10949      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10950      * process that block using the passed callback.
10951      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10952      * for the request to the remote server.
10953      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10954      * object into a block of Roo.data.Records.
10955      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10956      * The function must be passed <ul>
10957      * <li>The Record block object</li>
10958      * <li>The "arg" argument from the load function</li>
10959      * <li>A boolean success indicator</li>
10960      * </ul>
10961      * @param {Object} scope The scope in which to call the callback
10962      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10963      */
10964     load : function(params, reader, callback, scope, arg){
10965         if(this.fireEvent("beforeload", this, params) !== false){
10966             var  o = {
10967                 params : params || {},
10968                 request: {
10969                     callback : callback,
10970                     scope : scope,
10971                     arg : arg
10972                 },
10973                 reader: reader,
10974                 callback : this.loadResponse,
10975                 scope: this
10976             };
10977             if(this.useAjax){
10978                 Roo.applyIf(o, this.conn);
10979                 if(this.activeRequest){
10980                     Roo.Ajax.abort(this.activeRequest);
10981                 }
10982                 this.activeRequest = Roo.Ajax.request(o);
10983             }else{
10984                 this.conn.request(o);
10985             }
10986         }else{
10987             callback.call(scope||this, null, arg, false);
10988         }
10989     },
10990
10991     // private
10992     loadResponse : function(o, success, response){
10993         delete this.activeRequest;
10994         if(!success){
10995             this.fireEvent("loadexception", this, o, response);
10996             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10997             return;
10998         }
10999         var result;
11000         try {
11001             result = o.reader.read(response);
11002         }catch(e){
11003             this.fireEvent("loadexception", this, o, response, e);
11004             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11005             return;
11006         }
11007         
11008         this.fireEvent("load", this, o, o.request.arg);
11009         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11010     },
11011
11012     // private
11013     update : function(dataSet){
11014
11015     },
11016
11017     // private
11018     updateResponse : function(dataSet){
11019
11020     }
11021 });/*
11022  * Based on:
11023  * Ext JS Library 1.1.1
11024  * Copyright(c) 2006-2007, Ext JS, LLC.
11025  *
11026  * Originally Released Under LGPL - original licence link has changed is not relivant.
11027  *
11028  * Fork - LGPL
11029  * <script type="text/javascript">
11030  */
11031
11032 /**
11033  * @class Roo.data.ScriptTagProxy
11034  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11035  * other than the originating domain of the running page.<br><br>
11036  * <p>
11037  * <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
11038  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11039  * <p>
11040  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11041  * source code that is used as the source inside a &lt;script> tag.<br><br>
11042  * <p>
11043  * In order for the browser to process the returned data, the server must wrap the data object
11044  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11045  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11046  * depending on whether the callback name was passed:
11047  * <p>
11048  * <pre><code>
11049 boolean scriptTag = false;
11050 String cb = request.getParameter("callback");
11051 if (cb != null) {
11052     scriptTag = true;
11053     response.setContentType("text/javascript");
11054 } else {
11055     response.setContentType("application/x-json");
11056 }
11057 Writer out = response.getWriter();
11058 if (scriptTag) {
11059     out.write(cb + "(");
11060 }
11061 out.print(dataBlock.toJsonString());
11062 if (scriptTag) {
11063     out.write(");");
11064 }
11065 </pre></code>
11066  *
11067  * @constructor
11068  * @param {Object} config A configuration object.
11069  */
11070 Roo.data.ScriptTagProxy = function(config){
11071     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11072     Roo.apply(this, config);
11073     this.head = document.getElementsByTagName("head")[0];
11074 };
11075
11076 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11077
11078 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11079     /**
11080      * @cfg {String} url The URL from which to request the data object.
11081      */
11082     /**
11083      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11084      */
11085     timeout : 30000,
11086     /**
11087      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11088      * the server the name of the callback function set up by the load call to process the returned data object.
11089      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11090      * javascript output which calls this named function passing the data object as its only parameter.
11091      */
11092     callbackParam : "callback",
11093     /**
11094      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11095      * name to the request.
11096      */
11097     nocache : true,
11098
11099     /**
11100      * Load data from the configured URL, read the data object into
11101      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11102      * process that block using the passed callback.
11103      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11104      * for the request to the remote server.
11105      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11106      * object into a block of Roo.data.Records.
11107      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11108      * The function must be passed <ul>
11109      * <li>The Record block object</li>
11110      * <li>The "arg" argument from the load function</li>
11111      * <li>A boolean success indicator</li>
11112      * </ul>
11113      * @param {Object} scope The scope in which to call the callback
11114      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11115      */
11116     load : function(params, reader, callback, scope, arg){
11117         if(this.fireEvent("beforeload", this, params) !== false){
11118
11119             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11120
11121             var url = this.url;
11122             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11123             if(this.nocache){
11124                 url += "&_dc=" + (new Date().getTime());
11125             }
11126             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11127             var trans = {
11128                 id : transId,
11129                 cb : "stcCallback"+transId,
11130                 scriptId : "stcScript"+transId,
11131                 params : params,
11132                 arg : arg,
11133                 url : url,
11134                 callback : callback,
11135                 scope : scope,
11136                 reader : reader
11137             };
11138             var conn = this;
11139
11140             window[trans.cb] = function(o){
11141                 conn.handleResponse(o, trans);
11142             };
11143
11144             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11145
11146             if(this.autoAbort !== false){
11147                 this.abort();
11148             }
11149
11150             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11151
11152             var script = document.createElement("script");
11153             script.setAttribute("src", url);
11154             script.setAttribute("type", "text/javascript");
11155             script.setAttribute("id", trans.scriptId);
11156             this.head.appendChild(script);
11157
11158             this.trans = trans;
11159         }else{
11160             callback.call(scope||this, null, arg, false);
11161         }
11162     },
11163
11164     // private
11165     isLoading : function(){
11166         return this.trans ? true : false;
11167     },
11168
11169     /**
11170      * Abort the current server request.
11171      */
11172     abort : function(){
11173         if(this.isLoading()){
11174             this.destroyTrans(this.trans);
11175         }
11176     },
11177
11178     // private
11179     destroyTrans : function(trans, isLoaded){
11180         this.head.removeChild(document.getElementById(trans.scriptId));
11181         clearTimeout(trans.timeoutId);
11182         if(isLoaded){
11183             window[trans.cb] = undefined;
11184             try{
11185                 delete window[trans.cb];
11186             }catch(e){}
11187         }else{
11188             // if hasn't been loaded, wait for load to remove it to prevent script error
11189             window[trans.cb] = function(){
11190                 window[trans.cb] = undefined;
11191                 try{
11192                     delete window[trans.cb];
11193                 }catch(e){}
11194             };
11195         }
11196     },
11197
11198     // private
11199     handleResponse : function(o, trans){
11200         this.trans = false;
11201         this.destroyTrans(trans, true);
11202         var result;
11203         try {
11204             result = trans.reader.readRecords(o);
11205         }catch(e){
11206             this.fireEvent("loadexception", this, o, trans.arg, e);
11207             trans.callback.call(trans.scope||window, null, trans.arg, false);
11208             return;
11209         }
11210         this.fireEvent("load", this, o, trans.arg);
11211         trans.callback.call(trans.scope||window, result, trans.arg, true);
11212     },
11213
11214     // private
11215     handleFailure : function(trans){
11216         this.trans = false;
11217         this.destroyTrans(trans, false);
11218         this.fireEvent("loadexception", this, null, trans.arg);
11219         trans.callback.call(trans.scope||window, null, trans.arg, false);
11220     }
11221 });/*
11222  * Based on:
11223  * Ext JS Library 1.1.1
11224  * Copyright(c) 2006-2007, Ext JS, LLC.
11225  *
11226  * Originally Released Under LGPL - original licence link has changed is not relivant.
11227  *
11228  * Fork - LGPL
11229  * <script type="text/javascript">
11230  */
11231
11232 /**
11233  * @class Roo.data.JsonReader
11234  * @extends Roo.data.DataReader
11235  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11236  * based on mappings in a provided Roo.data.Record constructor.
11237  * 
11238  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11239  * in the reply previously. 
11240  * 
11241  * <p>
11242  * Example code:
11243  * <pre><code>
11244 var RecordDef = Roo.data.Record.create([
11245     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11246     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11247 ]);
11248 var myReader = new Roo.data.JsonReader({
11249     totalProperty: "results",    // The property which contains the total dataset size (optional)
11250     root: "rows",                // The property which contains an Array of row objects
11251     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11252 }, RecordDef);
11253 </code></pre>
11254  * <p>
11255  * This would consume a JSON file like this:
11256  * <pre><code>
11257 { 'results': 2, 'rows': [
11258     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11259     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11260 }
11261 </code></pre>
11262  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11263  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11264  * paged from the remote server.
11265  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11266  * @cfg {String} root name of the property which contains the Array of row objects.
11267  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11268  * @cfg {Array} fields Array of field definition objects
11269  * @constructor
11270  * Create a new JsonReader
11271  * @param {Object} meta Metadata configuration options
11272  * @param {Object} recordType Either an Array of field definition objects,
11273  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11274  */
11275 Roo.data.JsonReader = function(meta, recordType){
11276     
11277     meta = meta || {};
11278     // set some defaults:
11279     Roo.applyIf(meta, {
11280         totalProperty: 'total',
11281         successProperty : 'success',
11282         root : 'data',
11283         id : 'id'
11284     });
11285     
11286     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11287 };
11288 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11289     
11290     /**
11291      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11292      * Used by Store query builder to append _requestMeta to params.
11293      * 
11294      */
11295     metaFromRemote : false,
11296     /**
11297      * This method is only used by a DataProxy which has retrieved data from a remote server.
11298      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11299      * @return {Object} data A data block which is used by an Roo.data.Store object as
11300      * a cache of Roo.data.Records.
11301      */
11302     read : function(response){
11303         var json = response.responseText;
11304        
11305         var o = /* eval:var:o */ eval("("+json+")");
11306         if(!o) {
11307             throw {message: "JsonReader.read: Json object not found"};
11308         }
11309         
11310         if(o.metaData){
11311             
11312             delete this.ef;
11313             this.metaFromRemote = true;
11314             this.meta = o.metaData;
11315             this.recordType = Roo.data.Record.create(o.metaData.fields);
11316             this.onMetaChange(this.meta, this.recordType, o);
11317         }
11318         return this.readRecords(o);
11319     },
11320
11321     // private function a store will implement
11322     onMetaChange : function(meta, recordType, o){
11323
11324     },
11325
11326     /**
11327          * @ignore
11328          */
11329     simpleAccess: function(obj, subsc) {
11330         return obj[subsc];
11331     },
11332
11333         /**
11334          * @ignore
11335          */
11336     getJsonAccessor: function(){
11337         var re = /[\[\.]/;
11338         return function(expr) {
11339             try {
11340                 return(re.test(expr))
11341                     ? new Function("obj", "return obj." + expr)
11342                     : function(obj){
11343                         return obj[expr];
11344                     };
11345             } catch(e){}
11346             return Roo.emptyFn;
11347         };
11348     }(),
11349
11350     /**
11351      * Create a data block containing Roo.data.Records from an XML document.
11352      * @param {Object} o An object which contains an Array of row objects in the property specified
11353      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11354      * which contains the total size of the dataset.
11355      * @return {Object} data A data block which is used by an Roo.data.Store object as
11356      * a cache of Roo.data.Records.
11357      */
11358     readRecords : function(o){
11359         /**
11360          * After any data loads, the raw JSON data is available for further custom processing.
11361          * @type Object
11362          */
11363         this.o = o;
11364         var s = this.meta, Record = this.recordType,
11365             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11366
11367 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11368         if (!this.ef) {
11369             if(s.totalProperty) {
11370                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11371                 }
11372                 if(s.successProperty) {
11373                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11374                 }
11375                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11376                 if (s.id) {
11377                         var g = this.getJsonAccessor(s.id);
11378                         this.getId = function(rec) {
11379                                 var r = g(rec);  
11380                                 return (r === undefined || r === "") ? null : r;
11381                         };
11382                 } else {
11383                         this.getId = function(){return null;};
11384                 }
11385             this.ef = [];
11386             for(var jj = 0; jj < fl; jj++){
11387                 f = fi[jj];
11388                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11389                 this.ef[jj] = this.getJsonAccessor(map);
11390             }
11391         }
11392
11393         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11394         if(s.totalProperty){
11395             var vt = parseInt(this.getTotal(o), 10);
11396             if(!isNaN(vt)){
11397                 totalRecords = vt;
11398             }
11399         }
11400         if(s.successProperty){
11401             var vs = this.getSuccess(o);
11402             if(vs === false || vs === 'false'){
11403                 success = false;
11404             }
11405         }
11406         var records = [];
11407         for(var i = 0; i < c; i++){
11408                 var n = root[i];
11409             var values = {};
11410             var id = this.getId(n);
11411             for(var j = 0; j < fl; j++){
11412                 f = fi[j];
11413             var v = this.ef[j](n);
11414             if (!f.convert) {
11415                 Roo.log('missing convert for ' + f.name);
11416                 Roo.log(f);
11417                 continue;
11418             }
11419             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11420             }
11421             var record = new Record(values, id);
11422             record.json = n;
11423             records[i] = record;
11424         }
11425         return {
11426             raw : o,
11427             success : success,
11428             records : records,
11429             totalRecords : totalRecords
11430         };
11431     }
11432 });/*
11433  * Based on:
11434  * Ext JS Library 1.1.1
11435  * Copyright(c) 2006-2007, Ext JS, LLC.
11436  *
11437  * Originally Released Under LGPL - original licence link has changed is not relivant.
11438  *
11439  * Fork - LGPL
11440  * <script type="text/javascript">
11441  */
11442
11443 /**
11444  * @class Roo.data.ArrayReader
11445  * @extends Roo.data.DataReader
11446  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11447  * Each element of that Array represents a row of data fields. The
11448  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11449  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11450  * <p>
11451  * Example code:.
11452  * <pre><code>
11453 var RecordDef = Roo.data.Record.create([
11454     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11455     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11456 ]);
11457 var myReader = new Roo.data.ArrayReader({
11458     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11459 }, RecordDef);
11460 </code></pre>
11461  * <p>
11462  * This would consume an Array like this:
11463  * <pre><code>
11464 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11465   </code></pre>
11466  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11467  * @constructor
11468  * Create a new JsonReader
11469  * @param {Object} meta Metadata configuration options.
11470  * @param {Object} recordType Either an Array of field definition objects
11471  * as specified to {@link Roo.data.Record#create},
11472  * or an {@link Roo.data.Record} object
11473  * created using {@link Roo.data.Record#create}.
11474  */
11475 Roo.data.ArrayReader = function(meta, recordType){
11476     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11477 };
11478
11479 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11480     /**
11481      * Create a data block containing Roo.data.Records from an XML document.
11482      * @param {Object} o An Array of row objects which represents the dataset.
11483      * @return {Object} data A data block which is used by an Roo.data.Store object as
11484      * a cache of Roo.data.Records.
11485      */
11486     readRecords : function(o){
11487         var sid = this.meta ? this.meta.id : null;
11488         var recordType = this.recordType, fields = recordType.prototype.fields;
11489         var records = [];
11490         var root = o;
11491             for(var i = 0; i < root.length; i++){
11492                     var n = root[i];
11493                 var values = {};
11494                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11495                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11496                 var f = fields.items[j];
11497                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11498                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11499                 v = f.convert(v);
11500                 values[f.name] = v;
11501             }
11502                 var record = new recordType(values, id);
11503                 record.json = n;
11504                 records[records.length] = record;
11505             }
11506             return {
11507                 records : records,
11508                 totalRecords : records.length
11509             };
11510     }
11511 });/*
11512  * - LGPL
11513  * * 
11514  */
11515
11516 /**
11517  * @class Roo.bootstrap.ComboBox
11518  * @extends Roo.bootstrap.TriggerField
11519  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11520  * @cfg {Boolean} append (true|false) default false
11521  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11522  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11523  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11524  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11525  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11526  * @cfg {Boolean} animate default true
11527  * @cfg {Boolean} emptyResultText only for touch device
11528  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11529  * @constructor
11530  * Create a new ComboBox.
11531  * @param {Object} config Configuration options
11532  */
11533 Roo.bootstrap.ComboBox = function(config){
11534     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11535     this.addEvents({
11536         /**
11537          * @event expand
11538          * Fires when the dropdown list is expanded
11539              * @param {Roo.bootstrap.ComboBox} combo This combo box
11540              */
11541         'expand' : true,
11542         /**
11543          * @event collapse
11544          * Fires when the dropdown list is collapsed
11545              * @param {Roo.bootstrap.ComboBox} combo This combo box
11546              */
11547         'collapse' : true,
11548         /**
11549          * @event beforeselect
11550          * Fires before a list item is selected. Return false to cancel the selection.
11551              * @param {Roo.bootstrap.ComboBox} combo This combo box
11552              * @param {Roo.data.Record} record The data record returned from the underlying store
11553              * @param {Number} index The index of the selected item in the dropdown list
11554              */
11555         'beforeselect' : true,
11556         /**
11557          * @event select
11558          * Fires when a list item is selected
11559              * @param {Roo.bootstrap.ComboBox} combo This combo box
11560              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11561              * @param {Number} index The index of the selected item in the dropdown list
11562              */
11563         'select' : true,
11564         /**
11565          * @event beforequery
11566          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11567          * The event object passed has these properties:
11568              * @param {Roo.bootstrap.ComboBox} combo This combo box
11569              * @param {String} query The query
11570              * @param {Boolean} forceAll true to force "all" query
11571              * @param {Boolean} cancel true to cancel the query
11572              * @param {Object} e The query event object
11573              */
11574         'beforequery': true,
11575          /**
11576          * @event add
11577          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11578              * @param {Roo.bootstrap.ComboBox} combo This combo box
11579              */
11580         'add' : true,
11581         /**
11582          * @event edit
11583          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11584              * @param {Roo.bootstrap.ComboBox} combo This combo box
11585              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11586              */
11587         'edit' : true,
11588         /**
11589          * @event remove
11590          * Fires when the remove value from the combobox array
11591              * @param {Roo.bootstrap.ComboBox} combo This combo box
11592              */
11593         'remove' : true,
11594         /**
11595          * @event specialfilter
11596          * Fires when specialfilter
11597             * @param {Roo.bootstrap.ComboBox} combo This combo box
11598             */
11599         'specialfilter' : true,
11600         /**
11601          * @event tick
11602          * Fires when tick the element
11603             * @param {Roo.bootstrap.ComboBox} combo This combo box
11604             */
11605         'tick' : true,
11606         /**
11607          * @event touchviewdisplay
11608          * Fires when touch view require special display (default is using displayField)
11609             * @param {Roo.bootstrap.ComboBox} combo This combo box
11610             * @param {Object} cfg set html .
11611             */
11612         'touchviewdisplay' : true
11613         
11614     });
11615     
11616     this.item = [];
11617     this.tickItems = [];
11618     
11619     this.selectedIndex = -1;
11620     if(this.mode == 'local'){
11621         if(config.queryDelay === undefined){
11622             this.queryDelay = 10;
11623         }
11624         if(config.minChars === undefined){
11625             this.minChars = 0;
11626         }
11627     }
11628 };
11629
11630 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11631      
11632     /**
11633      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11634      * rendering into an Roo.Editor, defaults to false)
11635      */
11636     /**
11637      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11638      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11639      */
11640     /**
11641      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11642      */
11643     /**
11644      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11645      * the dropdown list (defaults to undefined, with no header element)
11646      */
11647
11648      /**
11649      * @cfg {String/Roo.Template} tpl The template to use to render the output
11650      */
11651      
11652      /**
11653      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11654      */
11655     listWidth: undefined,
11656     /**
11657      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11658      * mode = 'remote' or 'text' if mode = 'local')
11659      */
11660     displayField: undefined,
11661     
11662     /**
11663      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11664      * mode = 'remote' or 'value' if mode = 'local'). 
11665      * Note: use of a valueField requires the user make a selection
11666      * in order for a value to be mapped.
11667      */
11668     valueField: undefined,
11669     
11670     
11671     /**
11672      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11673      * field's data value (defaults to the underlying DOM element's name)
11674      */
11675     hiddenName: undefined,
11676     /**
11677      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11678      */
11679     listClass: '',
11680     /**
11681      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11682      */
11683     selectedClass: 'active',
11684     
11685     /**
11686      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11687      */
11688     shadow:'sides',
11689     /**
11690      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11691      * anchor positions (defaults to 'tl-bl')
11692      */
11693     listAlign: 'tl-bl?',
11694     /**
11695      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11696      */
11697     maxHeight: 300,
11698     /**
11699      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11700      * query specified by the allQuery config option (defaults to 'query')
11701      */
11702     triggerAction: 'query',
11703     /**
11704      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11705      * (defaults to 4, does not apply if editable = false)
11706      */
11707     minChars : 4,
11708     /**
11709      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11710      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11711      */
11712     typeAhead: false,
11713     /**
11714      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11715      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11716      */
11717     queryDelay: 500,
11718     /**
11719      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11720      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11721      */
11722     pageSize: 0,
11723     /**
11724      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11725      * when editable = true (defaults to false)
11726      */
11727     selectOnFocus:false,
11728     /**
11729      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11730      */
11731     queryParam: 'query',
11732     /**
11733      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11734      * when mode = 'remote' (defaults to 'Loading...')
11735      */
11736     loadingText: 'Loading...',
11737     /**
11738      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11739      */
11740     resizable: false,
11741     /**
11742      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11743      */
11744     handleHeight : 8,
11745     /**
11746      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11747      * traditional select (defaults to true)
11748      */
11749     editable: true,
11750     /**
11751      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11752      */
11753     allQuery: '',
11754     /**
11755      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11756      */
11757     mode: 'remote',
11758     /**
11759      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11760      * listWidth has a higher value)
11761      */
11762     minListWidth : 70,
11763     /**
11764      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11765      * allow the user to set arbitrary text into the field (defaults to false)
11766      */
11767     forceSelection:false,
11768     /**
11769      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11770      * if typeAhead = true (defaults to 250)
11771      */
11772     typeAheadDelay : 250,
11773     /**
11774      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11775      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11776      */
11777     valueNotFoundText : undefined,
11778     /**
11779      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11780      */
11781     blockFocus : false,
11782     
11783     /**
11784      * @cfg {Boolean} disableClear Disable showing of clear button.
11785      */
11786     disableClear : false,
11787     /**
11788      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11789      */
11790     alwaysQuery : false,
11791     
11792     /**
11793      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11794      */
11795     multiple : false,
11796     
11797     /**
11798      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11799      */
11800     invalidClass : "has-warning",
11801     
11802     /**
11803      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11804      */
11805     validClass : "has-success",
11806     
11807     /**
11808      * @cfg {Boolean} specialFilter (true|false) special filter default false
11809      */
11810     specialFilter : false,
11811     
11812     /**
11813      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11814      */
11815     mobileTouchView : true,
11816     
11817     //private
11818     addicon : false,
11819     editicon: false,
11820     
11821     page: 0,
11822     hasQuery: false,
11823     append: false,
11824     loadNext: false,
11825     autoFocus : true,
11826     tickable : false,
11827     btnPosition : 'right',
11828     triggerList : true,
11829     showToggleBtn : true,
11830     animate : true,
11831     emptyResultText: 'Empty',
11832     triggerText : 'Select',
11833     
11834     // element that contains real text value.. (when hidden is used..)
11835     
11836     getAutoCreate : function()
11837     {
11838         var cfg = false;
11839         
11840         /*
11841          * Touch Devices
11842          */
11843         
11844         if(Roo.isTouch && this.mobileTouchView){
11845             cfg = this.getAutoCreateTouchView();
11846             return cfg;;
11847         }
11848         
11849         /*
11850          *  Normal ComboBox
11851          */
11852         if(!this.tickable){
11853             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11854             return cfg;
11855         }
11856         
11857         /*
11858          *  ComboBox with tickable selections
11859          */
11860              
11861         var align = this.labelAlign || this.parentLabelAlign();
11862         
11863         cfg = {
11864             cls : 'form-group roo-combobox-tickable' //input-group
11865         };
11866         
11867         var buttons = {
11868             tag : 'div',
11869             cls : 'tickable-buttons',
11870             cn : [
11871                 {
11872                     tag : 'button',
11873                     type : 'button',
11874                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11875                     html : this.triggerText
11876                 },
11877                 {
11878                     tag : 'button',
11879                     type : 'button',
11880                     name : 'ok',
11881                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11882                     html : 'Done'
11883                 },
11884                 {
11885                     tag : 'button',
11886                     type : 'button',
11887                     name : 'cancel',
11888                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11889                     html : 'Cancel'
11890                 }
11891             ]
11892         };
11893         
11894         if(this.editable){
11895             buttons.cn.unshift({
11896                 tag: 'input',
11897                 cls: 'select2-search-field-input'
11898             });
11899         }
11900         
11901         var _this = this;
11902         
11903         Roo.each(buttons.cn, function(c){
11904             if (_this.size) {
11905                 c.cls += ' btn-' + _this.size;
11906             }
11907
11908             if (_this.disabled) {
11909                 c.disabled = true;
11910             }
11911         });
11912         
11913         var box = {
11914             tag: 'div',
11915             cn: [
11916                 {
11917                     tag: 'input',
11918                     type : 'hidden',
11919                     cls: 'form-hidden-field'
11920                 },
11921                 {
11922                     tag: 'ul',
11923                     cls: 'select2-choices',
11924                     cn:[
11925                         {
11926                             tag: 'li',
11927                             cls: 'select2-search-field',
11928                             cn: [
11929
11930                                 buttons
11931                             ]
11932                         }
11933                     ]
11934                 }
11935             ]
11936         };
11937         
11938         var combobox = {
11939             cls: 'select2-container input-group select2-container-multi',
11940             cn: [
11941                 box
11942 //                {
11943 //                    tag: 'ul',
11944 //                    cls: 'typeahead typeahead-long dropdown-menu',
11945 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11946 //                }
11947             ]
11948         };
11949         
11950         if(this.hasFeedback && !this.allowBlank){
11951             
11952             var feedback = {
11953                 tag: 'span',
11954                 cls: 'glyphicon form-control-feedback'
11955             };
11956
11957             combobox.cn.push(feedback);
11958         }
11959         
11960         if (align ==='left' && this.fieldLabel.length) {
11961             
11962 //                Roo.log("left and has label");
11963                 cfg.cn = [
11964                     
11965                     {
11966                         tag: 'label',
11967                         'for' :  id,
11968                         cls : 'control-label col-sm-' + this.labelWidth,
11969                         html : this.fieldLabel
11970                         
11971                     },
11972                     {
11973                         cls : "col-sm-" + (12 - this.labelWidth), 
11974                         cn: [
11975                             combobox
11976                         ]
11977                     }
11978                     
11979                 ];
11980         } else if ( this.fieldLabel.length) {
11981 //                Roo.log(" label");
11982                  cfg.cn = [
11983                    
11984                     {
11985                         tag: 'label',
11986                         //cls : 'input-group-addon',
11987                         html : this.fieldLabel
11988                         
11989                     },
11990                     
11991                     combobox
11992                     
11993                 ];
11994
11995         } else {
11996             
11997 //                Roo.log(" no label && no align");
11998                 cfg = combobox
11999                      
12000                 
12001         }
12002          
12003         var settings=this;
12004         ['xs','sm','md','lg'].map(function(size){
12005             if (settings[size]) {
12006                 cfg.cls += ' col-' + size + '-' + settings[size];
12007             }
12008         });
12009         
12010         return cfg;
12011         
12012     },
12013     
12014     _initEventsCalled : false,
12015     
12016     // private
12017     initEvents: function()
12018     {
12019         
12020         if (this._initEventsCalled) { // as we call render... prevent looping...
12021             return;
12022         }
12023         this._initEventsCalled = true;
12024         
12025         if (!this.store) {
12026             throw "can not find store for combo";
12027         }
12028         
12029         this.store = Roo.factory(this.store, Roo.data);
12030         
12031         // if we are building from html. then this element is so complex, that we can not really
12032         // use the rendered HTML.
12033         // so we have to trash and replace the previous code.
12034         if (Roo.XComponent.build_from_html) {
12035             
12036             // remove this element....
12037             var e = this.el.dom, k=0;
12038             while (e ) { e = e.previousSibling;  ++k;}
12039
12040             this.el.remove();
12041             
12042             this.el=false;
12043             this.rendered = false;
12044             
12045             this.render(this.parent().getChildContainer(true), k);
12046             
12047             
12048             
12049         }
12050         
12051         
12052         /*
12053          * Touch Devices
12054          */
12055         
12056         if(Roo.isTouch && this.mobileTouchView){
12057             this.initTouchView();
12058             return;
12059         }
12060         
12061         if(this.tickable){
12062             this.initTickableEvents();
12063             return;
12064         }
12065         
12066         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12067         
12068         if(this.hiddenName){
12069             
12070             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12071             
12072             this.hiddenField.dom.value =
12073                 this.hiddenValue !== undefined ? this.hiddenValue :
12074                 this.value !== undefined ? this.value : '';
12075
12076             // prevent input submission
12077             this.el.dom.removeAttribute('name');
12078             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12079              
12080              
12081         }
12082         //if(Roo.isGecko){
12083         //    this.el.dom.setAttribute('autocomplete', 'off');
12084         //}
12085         
12086         var cls = 'x-combo-list';
12087         
12088         //this.list = new Roo.Layer({
12089         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12090         //});
12091         
12092         var _this = this;
12093         
12094         (function(){
12095             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12096             _this.list.setWidth(lw);
12097         }).defer(100);
12098         
12099         this.list.on('mouseover', this.onViewOver, this);
12100         this.list.on('mousemove', this.onViewMove, this);
12101         
12102         this.list.on('scroll', this.onViewScroll, this);
12103         
12104         /*
12105         this.list.swallowEvent('mousewheel');
12106         this.assetHeight = 0;
12107
12108         if(this.title){
12109             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12110             this.assetHeight += this.header.getHeight();
12111         }
12112
12113         this.innerList = this.list.createChild({cls:cls+'-inner'});
12114         this.innerList.on('mouseover', this.onViewOver, this);
12115         this.innerList.on('mousemove', this.onViewMove, this);
12116         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12117         
12118         if(this.allowBlank && !this.pageSize && !this.disableClear){
12119             this.footer = this.list.createChild({cls:cls+'-ft'});
12120             this.pageTb = new Roo.Toolbar(this.footer);
12121            
12122         }
12123         if(this.pageSize){
12124             this.footer = this.list.createChild({cls:cls+'-ft'});
12125             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12126                     {pageSize: this.pageSize});
12127             
12128         }
12129         
12130         if (this.pageTb && this.allowBlank && !this.disableClear) {
12131             var _this = this;
12132             this.pageTb.add(new Roo.Toolbar.Fill(), {
12133                 cls: 'x-btn-icon x-btn-clear',
12134                 text: '&#160;',
12135                 handler: function()
12136                 {
12137                     _this.collapse();
12138                     _this.clearValue();
12139                     _this.onSelect(false, -1);
12140                 }
12141             });
12142         }
12143         if (this.footer) {
12144             this.assetHeight += this.footer.getHeight();
12145         }
12146         */
12147             
12148         if(!this.tpl){
12149             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12150         }
12151
12152         this.view = new Roo.View(this.list, this.tpl, {
12153             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12154         });
12155         //this.view.wrapEl.setDisplayed(false);
12156         this.view.on('click', this.onViewClick, this);
12157         
12158         
12159         
12160         this.store.on('beforeload', this.onBeforeLoad, this);
12161         this.store.on('load', this.onLoad, this);
12162         this.store.on('loadexception', this.onLoadException, this);
12163         /*
12164         if(this.resizable){
12165             this.resizer = new Roo.Resizable(this.list,  {
12166                pinned:true, handles:'se'
12167             });
12168             this.resizer.on('resize', function(r, w, h){
12169                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12170                 this.listWidth = w;
12171                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12172                 this.restrictHeight();
12173             }, this);
12174             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12175         }
12176         */
12177         if(!this.editable){
12178             this.editable = true;
12179             this.setEditable(false);
12180         }
12181         
12182         /*
12183         
12184         if (typeof(this.events.add.listeners) != 'undefined') {
12185             
12186             this.addicon = this.wrap.createChild(
12187                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12188        
12189             this.addicon.on('click', function(e) {
12190                 this.fireEvent('add', this);
12191             }, this);
12192         }
12193         if (typeof(this.events.edit.listeners) != 'undefined') {
12194             
12195             this.editicon = this.wrap.createChild(
12196                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12197             if (this.addicon) {
12198                 this.editicon.setStyle('margin-left', '40px');
12199             }
12200             this.editicon.on('click', function(e) {
12201                 
12202                 // we fire even  if inothing is selected..
12203                 this.fireEvent('edit', this, this.lastData );
12204                 
12205             }, this);
12206         }
12207         */
12208         
12209         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12210             "up" : function(e){
12211                 this.inKeyMode = true;
12212                 this.selectPrev();
12213             },
12214
12215             "down" : function(e){
12216                 if(!this.isExpanded()){
12217                     this.onTriggerClick();
12218                 }else{
12219                     this.inKeyMode = true;
12220                     this.selectNext();
12221                 }
12222             },
12223
12224             "enter" : function(e){
12225 //                this.onViewClick();
12226                 //return true;
12227                 this.collapse();
12228                 
12229                 if(this.fireEvent("specialkey", this, e)){
12230                     this.onViewClick(false);
12231                 }
12232                 
12233                 return true;
12234             },
12235
12236             "esc" : function(e){
12237                 this.collapse();
12238             },
12239
12240             "tab" : function(e){
12241                 this.collapse();
12242                 
12243                 if(this.fireEvent("specialkey", this, e)){
12244                     this.onViewClick(false);
12245                 }
12246                 
12247                 return true;
12248             },
12249
12250             scope : this,
12251
12252             doRelay : function(foo, bar, hname){
12253                 if(hname == 'down' || this.scope.isExpanded()){
12254                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12255                 }
12256                 return true;
12257             },
12258
12259             forceKeyDown: true
12260         });
12261         
12262         
12263         this.queryDelay = Math.max(this.queryDelay || 10,
12264                 this.mode == 'local' ? 10 : 250);
12265         
12266         
12267         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12268         
12269         if(this.typeAhead){
12270             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12271         }
12272         if(this.editable !== false){
12273             this.inputEl().on("keyup", this.onKeyUp, this);
12274         }
12275         if(this.forceSelection){
12276             this.inputEl().on('blur', this.doForce, this);
12277         }
12278         
12279         if(this.multiple){
12280             this.choices = this.el.select('ul.select2-choices', true).first();
12281             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12282         }
12283     },
12284     
12285     initTickableEvents: function()
12286     {   
12287         this.createList();
12288         
12289         if(this.hiddenName){
12290             
12291             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12292             
12293             this.hiddenField.dom.value =
12294                 this.hiddenValue !== undefined ? this.hiddenValue :
12295                 this.value !== undefined ? this.value : '';
12296
12297             // prevent input submission
12298             this.el.dom.removeAttribute('name');
12299             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12300              
12301              
12302         }
12303         
12304 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12305         
12306         this.choices = this.el.select('ul.select2-choices', true).first();
12307         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12308         if(this.triggerList){
12309             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12310         }
12311          
12312         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12313         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12314         
12315         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12316         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12317         
12318         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12319         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12320         
12321         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12322         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12323         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12324         
12325         this.okBtn.hide();
12326         this.cancelBtn.hide();
12327         
12328         var _this = this;
12329         
12330         (function(){
12331             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12332             _this.list.setWidth(lw);
12333         }).defer(100);
12334         
12335         this.list.on('mouseover', this.onViewOver, this);
12336         this.list.on('mousemove', this.onViewMove, this);
12337         
12338         this.list.on('scroll', this.onViewScroll, this);
12339         
12340         if(!this.tpl){
12341             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>';
12342         }
12343
12344         this.view = new Roo.View(this.list, this.tpl, {
12345             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12346         });
12347         
12348         //this.view.wrapEl.setDisplayed(false);
12349         this.view.on('click', this.onViewClick, this);
12350         
12351         
12352         
12353         this.store.on('beforeload', this.onBeforeLoad, this);
12354         this.store.on('load', this.onLoad, this);
12355         this.store.on('loadexception', this.onLoadException, this);
12356         
12357         if(this.editable){
12358             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12359                 "up" : function(e){
12360                     this.inKeyMode = true;
12361                     this.selectPrev();
12362                 },
12363
12364                 "down" : function(e){
12365                     this.inKeyMode = true;
12366                     this.selectNext();
12367                 },
12368
12369                 "enter" : function(e){
12370                     if(this.fireEvent("specialkey", this, e)){
12371                         this.onViewClick(false);
12372                     }
12373                     
12374                     return true;
12375                 },
12376
12377                 "esc" : function(e){
12378                     this.onTickableFooterButtonClick(e, false, false);
12379                 },
12380
12381                 "tab" : function(e){
12382                     this.fireEvent("specialkey", this, e);
12383                     
12384                     this.onTickableFooterButtonClick(e, false, false);
12385                     
12386                     return true;
12387                 },
12388
12389                 scope : this,
12390
12391                 doRelay : function(e, fn, key){
12392                     if(this.scope.isExpanded()){
12393                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12394                     }
12395                     return true;
12396                 },
12397
12398                 forceKeyDown: true
12399             });
12400         }
12401         
12402         this.queryDelay = Math.max(this.queryDelay || 10,
12403                 this.mode == 'local' ? 10 : 250);
12404         
12405         
12406         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12407         
12408         if(this.typeAhead){
12409             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12410         }
12411         
12412         if(this.editable !== false){
12413             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12414         }
12415         
12416     },
12417
12418     onDestroy : function(){
12419         if(this.view){
12420             this.view.setStore(null);
12421             this.view.el.removeAllListeners();
12422             this.view.el.remove();
12423             this.view.purgeListeners();
12424         }
12425         if(this.list){
12426             this.list.dom.innerHTML  = '';
12427         }
12428         
12429         if(this.store){
12430             this.store.un('beforeload', this.onBeforeLoad, this);
12431             this.store.un('load', this.onLoad, this);
12432             this.store.un('loadexception', this.onLoadException, this);
12433         }
12434         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12435     },
12436
12437     // private
12438     fireKey : function(e){
12439         if(e.isNavKeyPress() && !this.list.isVisible()){
12440             this.fireEvent("specialkey", this, e);
12441         }
12442     },
12443
12444     // private
12445     onResize: function(w, h){
12446 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12447 //        
12448 //        if(typeof w != 'number'){
12449 //            // we do not handle it!?!?
12450 //            return;
12451 //        }
12452 //        var tw = this.trigger.getWidth();
12453 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12454 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12455 //        var x = w - tw;
12456 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12457 //            
12458 //        //this.trigger.setStyle('left', x+'px');
12459 //        
12460 //        if(this.list && this.listWidth === undefined){
12461 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12462 //            this.list.setWidth(lw);
12463 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12464 //        }
12465         
12466     
12467         
12468     },
12469
12470     /**
12471      * Allow or prevent the user from directly editing the field text.  If false is passed,
12472      * the user will only be able to select from the items defined in the dropdown list.  This method
12473      * is the runtime equivalent of setting the 'editable' config option at config time.
12474      * @param {Boolean} value True to allow the user to directly edit the field text
12475      */
12476     setEditable : function(value){
12477         if(value == this.editable){
12478             return;
12479         }
12480         this.editable = value;
12481         if(!value){
12482             this.inputEl().dom.setAttribute('readOnly', true);
12483             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12484             this.inputEl().addClass('x-combo-noedit');
12485         }else{
12486             this.inputEl().dom.setAttribute('readOnly', false);
12487             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12488             this.inputEl().removeClass('x-combo-noedit');
12489         }
12490     },
12491
12492     // private
12493     
12494     onBeforeLoad : function(combo,opts){
12495         if(!this.hasFocus){
12496             return;
12497         }
12498          if (!opts.add) {
12499             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12500          }
12501         this.restrictHeight();
12502         this.selectedIndex = -1;
12503     },
12504
12505     // private
12506     onLoad : function(){
12507         
12508         this.hasQuery = false;
12509         
12510         if(!this.hasFocus){
12511             return;
12512         }
12513         
12514         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12515             this.loading.hide();
12516         }
12517              
12518         if(this.store.getCount() > 0){
12519             this.expand();
12520             this.restrictHeight();
12521             if(this.lastQuery == this.allQuery){
12522                 if(this.editable && !this.tickable){
12523                     this.inputEl().dom.select();
12524                 }
12525                 
12526                 if(
12527                     !this.selectByValue(this.value, true) &&
12528                     this.autoFocus && 
12529                     (
12530                         !this.store.lastOptions ||
12531                         typeof(this.store.lastOptions.add) == 'undefined' || 
12532                         this.store.lastOptions.add != true
12533                     )
12534                 ){
12535                     this.select(0, true);
12536                 }
12537             }else{
12538                 if(this.autoFocus){
12539                     this.selectNext();
12540                 }
12541                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12542                     this.taTask.delay(this.typeAheadDelay);
12543                 }
12544             }
12545         }else{
12546             this.onEmptyResults();
12547         }
12548         
12549         //this.el.focus();
12550     },
12551     // private
12552     onLoadException : function()
12553     {
12554         this.hasQuery = false;
12555         
12556         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12557             this.loading.hide();
12558         }
12559         
12560         if(this.tickable && this.editable){
12561             return;
12562         }
12563         
12564         this.collapse();
12565         // only causes errors at present
12566         //Roo.log(this.store.reader.jsonData);
12567         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12568             // fixme
12569             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12570         //}
12571         
12572         
12573     },
12574     // private
12575     onTypeAhead : function(){
12576         if(this.store.getCount() > 0){
12577             var r = this.store.getAt(0);
12578             var newValue = r.data[this.displayField];
12579             var len = newValue.length;
12580             var selStart = this.getRawValue().length;
12581             
12582             if(selStart != len){
12583                 this.setRawValue(newValue);
12584                 this.selectText(selStart, newValue.length);
12585             }
12586         }
12587     },
12588
12589     // private
12590     onSelect : function(record, index){
12591         
12592         if(this.fireEvent('beforeselect', this, record, index) !== false){
12593         
12594             this.setFromData(index > -1 ? record.data : false);
12595             
12596             this.collapse();
12597             this.fireEvent('select', this, record, index);
12598         }
12599     },
12600
12601     /**
12602      * Returns the currently selected field value or empty string if no value is set.
12603      * @return {String} value The selected value
12604      */
12605     getValue : function(){
12606         
12607         if(this.multiple){
12608             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12609         }
12610         
12611         if(this.valueField){
12612             return typeof this.value != 'undefined' ? this.value : '';
12613         }else{
12614             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12615         }
12616     },
12617
12618     /**
12619      * Clears any text/value currently set in the field
12620      */
12621     clearValue : function(){
12622         if(this.hiddenField){
12623             this.hiddenField.dom.value = '';
12624         }
12625         this.value = '';
12626         this.setRawValue('');
12627         this.lastSelectionText = '';
12628         this.lastData = false;
12629         
12630         var close = this.closeTriggerEl();
12631         
12632         if(close){
12633             close.hide();
12634         }
12635         
12636     },
12637
12638     /**
12639      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12640      * will be displayed in the field.  If the value does not match the data value of an existing item,
12641      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12642      * Otherwise the field will be blank (although the value will still be set).
12643      * @param {String} value The value to match
12644      */
12645     setValue : function(v){
12646         if(this.multiple){
12647             this.syncValue();
12648             return;
12649         }
12650         
12651         var text = v;
12652         if(this.valueField){
12653             var r = this.findRecord(this.valueField, v);
12654             if(r){
12655                 text = r.data[this.displayField];
12656             }else if(this.valueNotFoundText !== undefined){
12657                 text = this.valueNotFoundText;
12658             }
12659         }
12660         this.lastSelectionText = text;
12661         if(this.hiddenField){
12662             this.hiddenField.dom.value = v;
12663         }
12664         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12665         this.value = v;
12666         
12667         var close = this.closeTriggerEl();
12668         
12669         if(close){
12670             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12671         }
12672     },
12673     /**
12674      * @property {Object} the last set data for the element
12675      */
12676     
12677     lastData : false,
12678     /**
12679      * Sets the value of the field based on a object which is related to the record format for the store.
12680      * @param {Object} value the value to set as. or false on reset?
12681      */
12682     setFromData : function(o){
12683         
12684         if(this.multiple){
12685             this.addItem(o);
12686             return;
12687         }
12688             
12689         var dv = ''; // display value
12690         var vv = ''; // value value..
12691         this.lastData = o;
12692         if (this.displayField) {
12693             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12694         } else {
12695             // this is an error condition!!!
12696             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12697         }
12698         
12699         if(this.valueField){
12700             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12701         }
12702         
12703         var close = this.closeTriggerEl();
12704         
12705         if(close){
12706             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12707         }
12708         
12709         if(this.hiddenField){
12710             this.hiddenField.dom.value = vv;
12711             
12712             this.lastSelectionText = dv;
12713             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12714             this.value = vv;
12715             return;
12716         }
12717         // no hidden field.. - we store the value in 'value', but still display
12718         // display field!!!!
12719         this.lastSelectionText = dv;
12720         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12721         this.value = vv;
12722         
12723         
12724         
12725     },
12726     // private
12727     reset : function(){
12728         // overridden so that last data is reset..
12729         
12730         if(this.multiple){
12731             this.clearItem();
12732             return;
12733         }
12734         
12735         this.setValue(this.originalValue);
12736         this.clearInvalid();
12737         this.lastData = false;
12738         if (this.view) {
12739             this.view.clearSelections();
12740         }
12741     },
12742     // private
12743     findRecord : function(prop, value){
12744         var record;
12745         if(this.store.getCount() > 0){
12746             this.store.each(function(r){
12747                 if(r.data[prop] == value){
12748                     record = r;
12749                     return false;
12750                 }
12751                 return true;
12752             });
12753         }
12754         return record;
12755     },
12756     
12757     getName: function()
12758     {
12759         // returns hidden if it's set..
12760         if (!this.rendered) {return ''};
12761         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12762         
12763     },
12764     // private
12765     onViewMove : function(e, t){
12766         this.inKeyMode = false;
12767     },
12768
12769     // private
12770     onViewOver : function(e, t){
12771         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12772             return;
12773         }
12774         var item = this.view.findItemFromChild(t);
12775         
12776         if(item){
12777             var index = this.view.indexOf(item);
12778             this.select(index, false);
12779         }
12780     },
12781
12782     // private
12783     onViewClick : function(view, doFocus, el, e)
12784     {
12785         var index = this.view.getSelectedIndexes()[0];
12786         
12787         var r = this.store.getAt(index);
12788         
12789         if(this.tickable){
12790             
12791             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12792                 return;
12793             }
12794             
12795             var rm = false;
12796             var _this = this;
12797             
12798             Roo.each(this.tickItems, function(v,k){
12799                 
12800                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12801                     Roo.log(v);
12802                     _this.tickItems.splice(k, 1);
12803                     
12804                     if(typeof(e) == 'undefined' && view == false){
12805                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12806                     }
12807                     
12808                     rm = true;
12809                     return;
12810                 }
12811             });
12812             
12813             if(rm){
12814                 return;
12815             }
12816             
12817             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12818                 this.tickItems.push(r.data);
12819             }
12820             
12821             if(typeof(e) == 'undefined' && view == false){
12822                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12823             }
12824                     
12825             return;
12826         }
12827         
12828         if(r){
12829             this.onSelect(r, index);
12830         }
12831         if(doFocus !== false && !this.blockFocus){
12832             this.inputEl().focus();
12833         }
12834     },
12835
12836     // private
12837     restrictHeight : function(){
12838         //this.innerList.dom.style.height = '';
12839         //var inner = this.innerList.dom;
12840         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12841         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12842         //this.list.beginUpdate();
12843         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12844         this.list.alignTo(this.inputEl(), this.listAlign);
12845         this.list.alignTo(this.inputEl(), this.listAlign);
12846         //this.list.endUpdate();
12847     },
12848
12849     // private
12850     onEmptyResults : function(){
12851         
12852         if(this.tickable && this.editable){
12853             this.restrictHeight();
12854             return;
12855         }
12856         
12857         this.collapse();
12858     },
12859
12860     /**
12861      * Returns true if the dropdown list is expanded, else false.
12862      */
12863     isExpanded : function(){
12864         return this.list.isVisible();
12865     },
12866
12867     /**
12868      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12869      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12870      * @param {String} value The data value of the item to select
12871      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12872      * selected item if it is not currently in view (defaults to true)
12873      * @return {Boolean} True if the value matched an item in the list, else false
12874      */
12875     selectByValue : function(v, scrollIntoView){
12876         if(v !== undefined && v !== null){
12877             var r = this.findRecord(this.valueField || this.displayField, v);
12878             if(r){
12879                 this.select(this.store.indexOf(r), scrollIntoView);
12880                 return true;
12881             }
12882         }
12883         return false;
12884     },
12885
12886     /**
12887      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12888      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12889      * @param {Number} index The zero-based index of the list item to select
12890      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12891      * selected item if it is not currently in view (defaults to true)
12892      */
12893     select : function(index, scrollIntoView){
12894         this.selectedIndex = index;
12895         this.view.select(index);
12896         if(scrollIntoView !== false){
12897             var el = this.view.getNode(index);
12898             /*
12899              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12900              */
12901             if(el){
12902                 this.list.scrollChildIntoView(el, false);
12903             }
12904         }
12905     },
12906
12907     // private
12908     selectNext : function(){
12909         var ct = this.store.getCount();
12910         if(ct > 0){
12911             if(this.selectedIndex == -1){
12912                 this.select(0);
12913             }else if(this.selectedIndex < ct-1){
12914                 this.select(this.selectedIndex+1);
12915             }
12916         }
12917     },
12918
12919     // private
12920     selectPrev : function(){
12921         var ct = this.store.getCount();
12922         if(ct > 0){
12923             if(this.selectedIndex == -1){
12924                 this.select(0);
12925             }else if(this.selectedIndex != 0){
12926                 this.select(this.selectedIndex-1);
12927             }
12928         }
12929     },
12930
12931     // private
12932     onKeyUp : function(e){
12933         if(this.editable !== false && !e.isSpecialKey()){
12934             this.lastKey = e.getKey();
12935             this.dqTask.delay(this.queryDelay);
12936         }
12937     },
12938
12939     // private
12940     validateBlur : function(){
12941         return !this.list || !this.list.isVisible();   
12942     },
12943
12944     // private
12945     initQuery : function(){
12946         
12947         var v = this.getRawValue();
12948         
12949         if(this.tickable && this.editable){
12950             v = this.tickableInputEl().getValue();
12951         }
12952         
12953         this.doQuery(v);
12954     },
12955
12956     // private
12957     doForce : function(){
12958         if(this.inputEl().dom.value.length > 0){
12959             this.inputEl().dom.value =
12960                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12961              
12962         }
12963     },
12964
12965     /**
12966      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12967      * query allowing the query action to be canceled if needed.
12968      * @param {String} query The SQL query to execute
12969      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12970      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12971      * saved in the current store (defaults to false)
12972      */
12973     doQuery : function(q, forceAll){
12974         
12975         if(q === undefined || q === null){
12976             q = '';
12977         }
12978         var qe = {
12979             query: q,
12980             forceAll: forceAll,
12981             combo: this,
12982             cancel:false
12983         };
12984         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12985             return false;
12986         }
12987         q = qe.query;
12988         
12989         forceAll = qe.forceAll;
12990         if(forceAll === true || (q.length >= this.minChars)){
12991             
12992             this.hasQuery = true;
12993             
12994             if(this.lastQuery != q || this.alwaysQuery){
12995                 this.lastQuery = q;
12996                 if(this.mode == 'local'){
12997                     this.selectedIndex = -1;
12998                     if(forceAll){
12999                         this.store.clearFilter();
13000                     }else{
13001                         
13002                         if(this.specialFilter){
13003                             this.fireEvent('specialfilter', this);
13004                             this.onLoad();
13005                             return;
13006                         }
13007                         
13008                         this.store.filter(this.displayField, q);
13009                     }
13010                     
13011                     this.store.fireEvent("datachanged", this.store);
13012                     
13013                     this.onLoad();
13014                     
13015                     
13016                 }else{
13017                     
13018                     this.store.baseParams[this.queryParam] = q;
13019                     
13020                     var options = {params : this.getParams(q)};
13021                     
13022                     if(this.loadNext){
13023                         options.add = true;
13024                         options.params.start = this.page * this.pageSize;
13025                     }
13026                     
13027                     this.store.load(options);
13028                     
13029                     /*
13030                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13031                      *  we should expand the list on onLoad
13032                      *  so command out it
13033                      */
13034 //                    this.expand();
13035                 }
13036             }else{
13037                 this.selectedIndex = -1;
13038                 this.onLoad();   
13039             }
13040         }
13041         
13042         this.loadNext = false;
13043     },
13044     
13045     // private
13046     getParams : function(q){
13047         var p = {};
13048         //p[this.queryParam] = q;
13049         
13050         if(this.pageSize){
13051             p.start = 0;
13052             p.limit = this.pageSize;
13053         }
13054         return p;
13055     },
13056
13057     /**
13058      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13059      */
13060     collapse : function(){
13061         if(!this.isExpanded()){
13062             return;
13063         }
13064         
13065         this.list.hide();
13066         
13067         if(this.tickable){
13068             this.hasFocus = false;
13069             this.okBtn.hide();
13070             this.cancelBtn.hide();
13071             this.trigger.show();
13072             
13073             if(this.editable){
13074                 this.tickableInputEl().dom.value = '';
13075                 this.tickableInputEl().blur();
13076             }
13077             
13078         }
13079         
13080         Roo.get(document).un('mousedown', this.collapseIf, this);
13081         Roo.get(document).un('mousewheel', this.collapseIf, this);
13082         if (!this.editable) {
13083             Roo.get(document).un('keydown', this.listKeyPress, this);
13084         }
13085         this.fireEvent('collapse', this);
13086     },
13087
13088     // private
13089     collapseIf : function(e){
13090         var in_combo  = e.within(this.el);
13091         var in_list =  e.within(this.list);
13092         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13093         
13094         if (in_combo || in_list || is_list) {
13095             //e.stopPropagation();
13096             return;
13097         }
13098         
13099         if(this.tickable){
13100             this.onTickableFooterButtonClick(e, false, false);
13101         }
13102
13103         this.collapse();
13104         
13105     },
13106
13107     /**
13108      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13109      */
13110     expand : function(){
13111        
13112         if(this.isExpanded() || !this.hasFocus){
13113             return;
13114         }
13115         
13116         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13117         this.list.setWidth(lw);
13118         
13119         
13120          Roo.log('expand');
13121         
13122         this.list.show();
13123         
13124         this.restrictHeight();
13125         
13126         if(this.tickable){
13127             
13128             this.tickItems = Roo.apply([], this.item);
13129             
13130             this.okBtn.show();
13131             this.cancelBtn.show();
13132             this.trigger.hide();
13133             
13134             if(this.editable){
13135                 this.tickableInputEl().focus();
13136             }
13137             
13138         }
13139         
13140         Roo.get(document).on('mousedown', this.collapseIf, this);
13141         Roo.get(document).on('mousewheel', this.collapseIf, this);
13142         if (!this.editable) {
13143             Roo.get(document).on('keydown', this.listKeyPress, this);
13144         }
13145         
13146         this.fireEvent('expand', this);
13147     },
13148
13149     // private
13150     // Implements the default empty TriggerField.onTriggerClick function
13151     onTriggerClick : function(e)
13152     {
13153         Roo.log('trigger click');
13154         
13155         if(this.disabled || !this.triggerList){
13156             return;
13157         }
13158         
13159         this.page = 0;
13160         this.loadNext = false;
13161         
13162         if(this.isExpanded()){
13163             this.collapse();
13164             if (!this.blockFocus) {
13165                 this.inputEl().focus();
13166             }
13167             
13168         }else {
13169             this.hasFocus = true;
13170             if(this.triggerAction == 'all') {
13171                 this.doQuery(this.allQuery, true);
13172             } else {
13173                 this.doQuery(this.getRawValue());
13174             }
13175             if (!this.blockFocus) {
13176                 this.inputEl().focus();
13177             }
13178         }
13179     },
13180     
13181     onTickableTriggerClick : function(e)
13182     {
13183         if(this.disabled){
13184             return;
13185         }
13186         
13187         this.page = 0;
13188         this.loadNext = false;
13189         this.hasFocus = true;
13190         
13191         if(this.triggerAction == 'all') {
13192             this.doQuery(this.allQuery, true);
13193         } else {
13194             this.doQuery(this.getRawValue());
13195         }
13196     },
13197     
13198     onSearchFieldClick : function(e)
13199     {
13200         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13201             this.onTickableFooterButtonClick(e, false, false);
13202             return;
13203         }
13204         
13205         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13206             return;
13207         }
13208         
13209         this.page = 0;
13210         this.loadNext = false;
13211         this.hasFocus = true;
13212         
13213         if(this.triggerAction == 'all') {
13214             this.doQuery(this.allQuery, true);
13215         } else {
13216             this.doQuery(this.getRawValue());
13217         }
13218     },
13219     
13220     listKeyPress : function(e)
13221     {
13222         //Roo.log('listkeypress');
13223         // scroll to first matching element based on key pres..
13224         if (e.isSpecialKey()) {
13225             return false;
13226         }
13227         var k = String.fromCharCode(e.getKey()).toUpperCase();
13228         //Roo.log(k);
13229         var match  = false;
13230         var csel = this.view.getSelectedNodes();
13231         var cselitem = false;
13232         if (csel.length) {
13233             var ix = this.view.indexOf(csel[0]);
13234             cselitem  = this.store.getAt(ix);
13235             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13236                 cselitem = false;
13237             }
13238             
13239         }
13240         
13241         this.store.each(function(v) { 
13242             if (cselitem) {
13243                 // start at existing selection.
13244                 if (cselitem.id == v.id) {
13245                     cselitem = false;
13246                 }
13247                 return true;
13248             }
13249                 
13250             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13251                 match = this.store.indexOf(v);
13252                 return false;
13253             }
13254             return true;
13255         }, this);
13256         
13257         if (match === false) {
13258             return true; // no more action?
13259         }
13260         // scroll to?
13261         this.view.select(match);
13262         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13263         sn.scrollIntoView(sn.dom.parentNode, false);
13264     },
13265     
13266     onViewScroll : function(e, t){
13267         
13268         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){
13269             return;
13270         }
13271         
13272         this.hasQuery = true;
13273         
13274         this.loading = this.list.select('.loading', true).first();
13275         
13276         if(this.loading === null){
13277             this.list.createChild({
13278                 tag: 'div',
13279                 cls: 'loading select2-more-results select2-active',
13280                 html: 'Loading more results...'
13281             });
13282             
13283             this.loading = this.list.select('.loading', true).first();
13284             
13285             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13286             
13287             this.loading.hide();
13288         }
13289         
13290         this.loading.show();
13291         
13292         var _combo = this;
13293         
13294         this.page++;
13295         this.loadNext = true;
13296         
13297         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13298         
13299         return;
13300     },
13301     
13302     addItem : function(o)
13303     {   
13304         var dv = ''; // display value
13305         
13306         if (this.displayField) {
13307             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13308         } else {
13309             // this is an error condition!!!
13310             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13311         }
13312         
13313         if(!dv.length){
13314             return;
13315         }
13316         
13317         var choice = this.choices.createChild({
13318             tag: 'li',
13319             cls: 'select2-search-choice',
13320             cn: [
13321                 {
13322                     tag: 'div',
13323                     html: dv
13324                 },
13325                 {
13326                     tag: 'a',
13327                     href: '#',
13328                     cls: 'select2-search-choice-close',
13329                     tabindex: '-1'
13330                 }
13331             ]
13332             
13333         }, this.searchField);
13334         
13335         var close = choice.select('a.select2-search-choice-close', true).first();
13336         
13337         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13338         
13339         this.item.push(o);
13340         
13341         this.lastData = o;
13342         
13343         this.syncValue();
13344         
13345         this.inputEl().dom.value = '';
13346         
13347         this.validate();
13348     },
13349     
13350     onRemoveItem : function(e, _self, o)
13351     {
13352         e.preventDefault();
13353         
13354         this.lastItem = Roo.apply([], this.item);
13355         
13356         var index = this.item.indexOf(o.data) * 1;
13357         
13358         if( index < 0){
13359             Roo.log('not this item?!');
13360             return;
13361         }
13362         
13363         this.item.splice(index, 1);
13364         o.item.remove();
13365         
13366         this.syncValue();
13367         
13368         this.fireEvent('remove', this, e);
13369         
13370         this.validate();
13371         
13372     },
13373     
13374     syncValue : function()
13375     {
13376         if(!this.item.length){
13377             this.clearValue();
13378             return;
13379         }
13380             
13381         var value = [];
13382         var _this = this;
13383         Roo.each(this.item, function(i){
13384             if(_this.valueField){
13385                 value.push(i[_this.valueField]);
13386                 return;
13387             }
13388
13389             value.push(i);
13390         });
13391
13392         this.value = value.join(',');
13393
13394         if(this.hiddenField){
13395             this.hiddenField.dom.value = this.value;
13396         }
13397         
13398         this.store.fireEvent("datachanged", this.store);
13399     },
13400     
13401     clearItem : function()
13402     {
13403         if(!this.multiple){
13404             return;
13405         }
13406         
13407         this.item = [];
13408         
13409         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13410            c.remove();
13411         });
13412         
13413         this.syncValue();
13414         
13415         this.validate();
13416         
13417         if(this.tickable && !Roo.isTouch){
13418             this.view.refresh();
13419         }
13420     },
13421     
13422     inputEl: function ()
13423     {
13424         if(Roo.isTouch && this.mobileTouchView){
13425             return this.el.select('input.form-control',true).first();
13426         }
13427         
13428         if(this.tickable){
13429             return this.searchField;
13430         }
13431         
13432         return this.el.select('input.form-control',true).first();
13433     },
13434     
13435     
13436     onTickableFooterButtonClick : function(e, btn, el)
13437     {
13438         e.preventDefault();
13439         
13440         this.lastItem = Roo.apply([], this.item);
13441         
13442         if(btn && btn.name == 'cancel'){
13443             this.tickItems = Roo.apply([], this.item);
13444             this.collapse();
13445             return;
13446         }
13447         
13448         this.clearItem();
13449         
13450         var _this = this;
13451         
13452         Roo.each(this.tickItems, function(o){
13453             _this.addItem(o);
13454         });
13455         
13456         this.collapse();
13457         
13458     },
13459     
13460     validate : function()
13461     {
13462         var v = this.getRawValue();
13463         
13464         if(this.multiple){
13465             v = this.getValue();
13466         }
13467         
13468         if(this.disabled || this.allowBlank || v.length){
13469             this.markValid();
13470             return true;
13471         }
13472         
13473         this.markInvalid();
13474         return false;
13475     },
13476     
13477     tickableInputEl : function()
13478     {
13479         if(!this.tickable || !this.editable){
13480             return this.inputEl();
13481         }
13482         
13483         return this.inputEl().select('.select2-search-field-input', true).first();
13484     },
13485     
13486     
13487     getAutoCreateTouchView : function()
13488     {
13489         var id = Roo.id();
13490         
13491         var cfg = {
13492             cls: 'form-group' //input-group
13493         };
13494         
13495         var input =  {
13496             tag: 'input',
13497             id : id,
13498             type : this.inputType,
13499             cls : 'form-control x-combo-noedit',
13500             autocomplete: 'new-password',
13501             placeholder : this.placeholder || '',
13502             readonly : true
13503         };
13504         
13505         if (this.name) {
13506             input.name = this.name;
13507         }
13508         
13509         if (this.size) {
13510             input.cls += ' input-' + this.size;
13511         }
13512         
13513         if (this.disabled) {
13514             input.disabled = true;
13515         }
13516         
13517         var inputblock = {
13518             cls : '',
13519             cn : [
13520                 input
13521             ]
13522         };
13523         
13524         if(this.before){
13525             inputblock.cls += ' input-group';
13526             
13527             inputblock.cn.unshift({
13528                 tag :'span',
13529                 cls : 'input-group-addon',
13530                 html : this.before
13531             });
13532         }
13533         
13534         if(this.removable && !this.multiple){
13535             inputblock.cls += ' roo-removable';
13536             
13537             inputblock.cn.push({
13538                 tag: 'button',
13539                 html : 'x',
13540                 cls : 'roo-combo-removable-btn close'
13541             });
13542         }
13543
13544         if(this.hasFeedback && !this.allowBlank){
13545             
13546             inputblock.cls += ' has-feedback';
13547             
13548             inputblock.cn.push({
13549                 tag: 'span',
13550                 cls: 'glyphicon form-control-feedback'
13551             });
13552             
13553         }
13554         
13555         if (this.after) {
13556             
13557             inputblock.cls += (this.before) ? '' : ' input-group';
13558             
13559             inputblock.cn.push({
13560                 tag :'span',
13561                 cls : 'input-group-addon',
13562                 html : this.after
13563             });
13564         }
13565
13566         var box = {
13567             tag: 'div',
13568             cn: [
13569                 {
13570                     tag: 'input',
13571                     type : 'hidden',
13572                     cls: 'form-hidden-field'
13573                 },
13574                 inputblock
13575             ]
13576             
13577         };
13578         
13579         if(this.multiple){
13580             box = {
13581                 tag: 'div',
13582                 cn: [
13583                     {
13584                         tag: 'input',
13585                         type : 'hidden',
13586                         cls: 'form-hidden-field'
13587                     },
13588                     {
13589                         tag: 'ul',
13590                         cls: 'select2-choices',
13591                         cn:[
13592                             {
13593                                 tag: 'li',
13594                                 cls: 'select2-search-field',
13595                                 cn: [
13596
13597                                     inputblock
13598                                 ]
13599                             }
13600                         ]
13601                     }
13602                 ]
13603             }
13604         };
13605         
13606         var combobox = {
13607             cls: 'select2-container input-group',
13608             cn: [
13609                 box
13610             ]
13611         };
13612         
13613         if(this.multiple){
13614             combobox.cls += ' select2-container-multi';
13615         }
13616         
13617         var align = this.labelAlign || this.parentLabelAlign();
13618         
13619         cfg.cn = combobox;
13620         
13621         if(this.fieldLabel.length){
13622             
13623             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13624             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13625             
13626             cfg.cn = [
13627                 {
13628                     tag: 'label',
13629                     cls : 'control-label ' + lw,
13630                     html : this.fieldLabel
13631
13632                 },
13633                 {
13634                     cls : cw, 
13635                     cn: [
13636                         combobox
13637                     ]
13638                 }
13639             ];
13640         }
13641         
13642         var settings = this;
13643         
13644         ['xs','sm','md','lg'].map(function(size){
13645             if (settings[size]) {
13646                 cfg.cls += ' col-' + size + '-' + settings[size];
13647             }
13648         });
13649         
13650         return cfg;
13651     },
13652     
13653     initTouchView : function()
13654     {
13655         this.renderTouchView();
13656         
13657         this.touchViewEl.on('scroll', function(){
13658             this.el.dom.scrollTop = 0;
13659         }, this);
13660         
13661         this.originalValue = this.getValue();
13662         
13663         this.inputEl().on("click", this.showTouchView, this);
13664         
13665         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13666         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13667         
13668         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13669         
13670         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13671         this.store.on('load', this.onTouchViewLoad, this);
13672         this.store.on('loadexception', this.onTouchViewLoadException, this);
13673         
13674         if(this.hiddenName){
13675             
13676             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13677             
13678             this.hiddenField.dom.value =
13679                 this.hiddenValue !== undefined ? this.hiddenValue :
13680                 this.value !== undefined ? this.value : '';
13681         
13682             this.el.dom.removeAttribute('name');
13683             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13684         }
13685         
13686         if(this.multiple){
13687             this.choices = this.el.select('ul.select2-choices', true).first();
13688             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13689         }
13690         
13691         if(this.removable && !this.multiple){
13692             var close = this.closeTriggerEl();
13693             if(close){
13694                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13695                 close.on('click', this.removeBtnClick, this, close);
13696             }
13697         }
13698         /*
13699          * fix the bug in Safari iOS8
13700          */
13701         this.inputEl().on("focus", function(e){
13702             document.activeElement.blur();
13703         }, this);
13704         
13705         return;
13706         
13707         
13708     },
13709     
13710     renderTouchView : function()
13711     {
13712         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13713         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13714         
13715         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13716         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13717         
13718         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13719         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13720         this.touchViewBodyEl.setStyle('overflow', 'auto');
13721         
13722         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13723         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13724         
13725         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13726         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13727         
13728     },
13729     
13730     showTouchView : function()
13731     {
13732         if(this.disabled){
13733             return;
13734         }
13735         
13736         this.touchViewHeaderEl.hide();
13737
13738         if(this.fieldLabel.length){
13739             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13740             this.touchViewHeaderEl.show();
13741         }
13742
13743         this.touchViewEl.show();
13744
13745         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13746         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13747
13748         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13749
13750         if(this.fieldLabel.length){
13751             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13752         }
13753         
13754         this.touchViewBodyEl.setHeight(bodyHeight);
13755
13756         if(this.animate){
13757             var _this = this;
13758             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13759         }else{
13760             this.touchViewEl.addClass('in');
13761         }
13762
13763         this.doTouchViewQuery();
13764         
13765     },
13766     
13767     hideTouchView : function()
13768     {
13769         this.touchViewEl.removeClass('in');
13770
13771         if(this.animate){
13772             var _this = this;
13773             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13774         }else{
13775             this.touchViewEl.setStyle('display', 'none');
13776         }
13777         
13778     },
13779     
13780     setTouchViewValue : function()
13781     {
13782         if(this.multiple){
13783             this.clearItem();
13784         
13785             var _this = this;
13786
13787             Roo.each(this.tickItems, function(o){
13788                 this.addItem(o);
13789             }, this);
13790         }
13791         
13792         this.hideTouchView();
13793     },
13794     
13795     doTouchViewQuery : function()
13796     {
13797         var qe = {
13798             query: '',
13799             forceAll: true,
13800             combo: this,
13801             cancel:false
13802         };
13803         
13804         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13805             return false;
13806         }
13807         
13808         if(!this.alwaysQuery || this.mode == 'local'){
13809             this.onTouchViewLoad();
13810             return;
13811         }
13812         
13813         this.store.load();
13814     },
13815     
13816     onTouchViewBeforeLoad : function(combo,opts)
13817     {
13818         return;
13819     },
13820
13821     // private
13822     onTouchViewLoad : function()
13823     {
13824         if(this.store.getCount() < 1){
13825             this.onTouchViewEmptyResults();
13826             return;
13827         }
13828         
13829         this.clearTouchView();
13830         
13831         var rawValue = this.getRawValue();
13832         
13833         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13834         
13835         this.tickItems = [];
13836         
13837         this.store.data.each(function(d, rowIndex){
13838             var row = this.touchViewListGroup.createChild(template);
13839             
13840             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13841                 var cfg = {
13842                     data : d.data,
13843                     html : d.data[this.displayField]
13844                 };
13845                 
13846                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13847                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13848                 }
13849             }
13850             
13851             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13852                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13853             }
13854             
13855             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13856                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13857                 this.tickItems.push(d.data);
13858             }
13859             
13860             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13861             
13862         }, this);
13863         
13864         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13865         
13866         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13867
13868         if(this.fieldLabel.length){
13869             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13870         }
13871
13872         var listHeight = this.touchViewListGroup.getHeight();
13873         
13874         var _this = this;
13875         
13876         if(firstChecked && listHeight > bodyHeight){
13877             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13878         }
13879         
13880     },
13881     
13882     onTouchViewLoadException : function()
13883     {
13884         this.hideTouchView();
13885     },
13886     
13887     onTouchViewEmptyResults : function()
13888     {
13889         this.clearTouchView();
13890         
13891         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13892         
13893         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13894         
13895     },
13896     
13897     clearTouchView : function()
13898     {
13899         this.touchViewListGroup.dom.innerHTML = '';
13900     },
13901     
13902     onTouchViewClick : function(e, el, o)
13903     {
13904         e.preventDefault();
13905         
13906         var row = o.row;
13907         var rowIndex = o.rowIndex;
13908         
13909         var r = this.store.getAt(rowIndex);
13910         
13911         if(!this.multiple){
13912             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13913                 c.dom.removeAttribute('checked');
13914             }, this);
13915             
13916             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13917         
13918             this.setFromData(r.data);
13919             
13920             var close = this.closeTriggerEl();
13921         
13922             if(close){
13923                 close.show();
13924             }
13925
13926             this.hideTouchView();
13927             
13928             this.fireEvent('select', this, r, rowIndex);
13929             
13930             return;
13931         }
13932         
13933         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13934             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13935             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13936             return;
13937         }
13938         
13939         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13940         this.addItem(r.data);
13941         this.tickItems.push(r.data);
13942         
13943     }
13944     
13945
13946     /** 
13947     * @cfg {Boolean} grow 
13948     * @hide 
13949     */
13950     /** 
13951     * @cfg {Number} growMin 
13952     * @hide 
13953     */
13954     /** 
13955     * @cfg {Number} growMax 
13956     * @hide 
13957     */
13958     /**
13959      * @hide
13960      * @method autoSize
13961      */
13962 });
13963
13964 Roo.apply(Roo.bootstrap.ComboBox,  {
13965     
13966     header : {
13967         tag: 'div',
13968         cls: 'modal-header',
13969         cn: [
13970             {
13971                 tag: 'h4',
13972                 cls: 'modal-title'
13973             }
13974         ]
13975     },
13976     
13977     body : {
13978         tag: 'div',
13979         cls: 'modal-body',
13980         cn: [
13981             {
13982                 tag: 'ul',
13983                 cls: 'list-group'
13984             }
13985         ]
13986     },
13987     
13988     listItemRadio : {
13989         tag: 'li',
13990         cls: 'list-group-item',
13991         cn: [
13992             {
13993                 tag: 'span',
13994                 cls: 'roo-combobox-list-group-item-value'
13995             },
13996             {
13997                 tag: 'div',
13998                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13999                 cn: [
14000                     {
14001                         tag: 'input',
14002                         type: 'radio'
14003                     },
14004                     {
14005                         tag: 'label'
14006                     }
14007                 ]
14008             }
14009         ]
14010     },
14011     
14012     listItemCheckbox : {
14013         tag: 'li',
14014         cls: 'list-group-item',
14015         cn: [
14016             {
14017                 tag: 'span',
14018                 cls: 'roo-combobox-list-group-item-value'
14019             },
14020             {
14021                 tag: 'div',
14022                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14023                 cn: [
14024                     {
14025                         tag: 'input',
14026                         type: 'checkbox'
14027                     },
14028                     {
14029                         tag: 'label'
14030                     }
14031                 ]
14032             }
14033         ]
14034     },
14035     
14036     emptyResult : {
14037         tag: 'div',
14038         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14039     },
14040     
14041     footer : {
14042         tag: 'div',
14043         cls: 'modal-footer',
14044         cn: [
14045             {
14046                 tag: 'div',
14047                 cls: 'row',
14048                 cn: [
14049                     {
14050                         tag: 'div',
14051                         cls: 'col-xs-6 text-left',
14052                         cn: {
14053                             tag: 'button',
14054                             cls: 'btn btn-danger roo-touch-view-cancel',
14055                             html: 'Cancel'
14056                         }
14057                     },
14058                     {
14059                         tag: 'div',
14060                         cls: 'col-xs-6 text-right',
14061                         cn: {
14062                             tag: 'button',
14063                             cls: 'btn btn-success roo-touch-view-ok',
14064                             html: 'OK'
14065                         }
14066                     }
14067                 ]
14068             }
14069         ]
14070         
14071     }
14072 });
14073
14074 Roo.apply(Roo.bootstrap.ComboBox,  {
14075     
14076     touchViewTemplate : {
14077         tag: 'div',
14078         cls: 'modal fade roo-combobox-touch-view',
14079         cn: [
14080             {
14081                 tag: 'div',
14082                 cls: 'modal-dialog',
14083                 style : 'position:fixed', // we have to fix position....
14084                 cn: [
14085                     {
14086                         tag: 'div',
14087                         cls: 'modal-content',
14088                         cn: [
14089                             Roo.bootstrap.ComboBox.header,
14090                             Roo.bootstrap.ComboBox.body,
14091                             Roo.bootstrap.ComboBox.footer
14092                         ]
14093                     }
14094                 ]
14095             }
14096         ]
14097     }
14098 });/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.View
14111  * @extends Roo.util.Observable
14112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14113  * This class also supports single and multi selection modes. <br>
14114  * Create a data model bound view:
14115  <pre><code>
14116  var store = new Roo.data.Store(...);
14117
14118  var view = new Roo.View({
14119     el : "my-element",
14120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14121  
14122     singleSelect: true,
14123     selectedClass: "ydataview-selected",
14124     store: store
14125  });
14126
14127  // listen for node click?
14128  view.on("click", function(vw, index, node, e){
14129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14130  });
14131
14132  // load XML data
14133  dataModel.load("foobar.xml");
14134  </code></pre>
14135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14136  * <br><br>
14137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14139  * 
14140  * Note: old style constructor is still suported (container, template, config)
14141  * 
14142  * @constructor
14143  * Create a new View
14144  * @param {Object} config The config object
14145  * 
14146  */
14147 Roo.View = function(config, depreciated_tpl, depreciated_config){
14148     
14149     this.parent = false;
14150     
14151     if (typeof(depreciated_tpl) == 'undefined') {
14152         // new way.. - universal constructor.
14153         Roo.apply(this, config);
14154         this.el  = Roo.get(this.el);
14155     } else {
14156         // old format..
14157         this.el  = Roo.get(config);
14158         this.tpl = depreciated_tpl;
14159         Roo.apply(this, depreciated_config);
14160     }
14161     this.wrapEl  = this.el.wrap().wrap();
14162     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14163     
14164     
14165     if(typeof(this.tpl) == "string"){
14166         this.tpl = new Roo.Template(this.tpl);
14167     } else {
14168         // support xtype ctors..
14169         this.tpl = new Roo.factory(this.tpl, Roo);
14170     }
14171     
14172     
14173     this.tpl.compile();
14174     
14175     /** @private */
14176     this.addEvents({
14177         /**
14178          * @event beforeclick
14179          * Fires before a click is processed. Returns false to cancel the default action.
14180          * @param {Roo.View} this
14181          * @param {Number} index The index of the target node
14182          * @param {HTMLElement} node The target node
14183          * @param {Roo.EventObject} e The raw event object
14184          */
14185             "beforeclick" : true,
14186         /**
14187          * @event click
14188          * Fires when a template node is clicked.
14189          * @param {Roo.View} this
14190          * @param {Number} index The index of the target node
14191          * @param {HTMLElement} node The target node
14192          * @param {Roo.EventObject} e The raw event object
14193          */
14194             "click" : true,
14195         /**
14196          * @event dblclick
14197          * Fires when a template node is double clicked.
14198          * @param {Roo.View} this
14199          * @param {Number} index The index of the target node
14200          * @param {HTMLElement} node The target node
14201          * @param {Roo.EventObject} e The raw event object
14202          */
14203             "dblclick" : true,
14204         /**
14205          * @event contextmenu
14206          * Fires when a template node is right clicked.
14207          * @param {Roo.View} this
14208          * @param {Number} index The index of the target node
14209          * @param {HTMLElement} node The target node
14210          * @param {Roo.EventObject} e The raw event object
14211          */
14212             "contextmenu" : true,
14213         /**
14214          * @event selectionchange
14215          * Fires when the selected nodes change.
14216          * @param {Roo.View} this
14217          * @param {Array} selections Array of the selected nodes
14218          */
14219             "selectionchange" : true,
14220     
14221         /**
14222          * @event beforeselect
14223          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14224          * @param {Roo.View} this
14225          * @param {HTMLElement} node The node to be selected
14226          * @param {Array} selections Array of currently selected nodes
14227          */
14228             "beforeselect" : true,
14229         /**
14230          * @event preparedata
14231          * Fires on every row to render, to allow you to change the data.
14232          * @param {Roo.View} this
14233          * @param {Object} data to be rendered (change this)
14234          */
14235           "preparedata" : true
14236           
14237           
14238         });
14239
14240
14241
14242     this.el.on({
14243         "click": this.onClick,
14244         "dblclick": this.onDblClick,
14245         "contextmenu": this.onContextMenu,
14246         scope:this
14247     });
14248
14249     this.selections = [];
14250     this.nodes = [];
14251     this.cmp = new Roo.CompositeElementLite([]);
14252     if(this.store){
14253         this.store = Roo.factory(this.store, Roo.data);
14254         this.setStore(this.store, true);
14255     }
14256     
14257     if ( this.footer && this.footer.xtype) {
14258            
14259          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14260         
14261         this.footer.dataSource = this.store;
14262         this.footer.container = fctr;
14263         this.footer = Roo.factory(this.footer, Roo);
14264         fctr.insertFirst(this.el);
14265         
14266         // this is a bit insane - as the paging toolbar seems to detach the el..
14267 //        dom.parentNode.parentNode.parentNode
14268          // they get detached?
14269     }
14270     
14271     
14272     Roo.View.superclass.constructor.call(this);
14273     
14274     
14275 };
14276
14277 Roo.extend(Roo.View, Roo.util.Observable, {
14278     
14279      /**
14280      * @cfg {Roo.data.Store} store Data store to load data from.
14281      */
14282     store : false,
14283     
14284     /**
14285      * @cfg {String|Roo.Element} el The container element.
14286      */
14287     el : '',
14288     
14289     /**
14290      * @cfg {String|Roo.Template} tpl The template used by this View 
14291      */
14292     tpl : false,
14293     /**
14294      * @cfg {String} dataName the named area of the template to use as the data area
14295      *                          Works with domtemplates roo-name="name"
14296      */
14297     dataName: false,
14298     /**
14299      * @cfg {String} selectedClass The css class to add to selected nodes
14300      */
14301     selectedClass : "x-view-selected",
14302      /**
14303      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14304      */
14305     emptyText : "",
14306     
14307     /**
14308      * @cfg {String} text to display on mask (default Loading)
14309      */
14310     mask : false,
14311     /**
14312      * @cfg {Boolean} multiSelect Allow multiple selection
14313      */
14314     multiSelect : false,
14315     /**
14316      * @cfg {Boolean} singleSelect Allow single selection
14317      */
14318     singleSelect:  false,
14319     
14320     /**
14321      * @cfg {Boolean} toggleSelect - selecting 
14322      */
14323     toggleSelect : false,
14324     
14325     /**
14326      * @cfg {Boolean} tickable - selecting 
14327      */
14328     tickable : false,
14329     
14330     /**
14331      * Returns the element this view is bound to.
14332      * @return {Roo.Element}
14333      */
14334     getEl : function(){
14335         return this.wrapEl;
14336     },
14337     
14338     
14339
14340     /**
14341      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14342      */
14343     refresh : function(){
14344         //Roo.log('refresh');
14345         var t = this.tpl;
14346         
14347         // if we are using something like 'domtemplate', then
14348         // the what gets used is:
14349         // t.applySubtemplate(NAME, data, wrapping data..)
14350         // the outer template then get' applied with
14351         //     the store 'extra data'
14352         // and the body get's added to the
14353         //      roo-name="data" node?
14354         //      <span class='roo-tpl-{name}'></span> ?????
14355         
14356         
14357         
14358         this.clearSelections();
14359         this.el.update("");
14360         var html = [];
14361         var records = this.store.getRange();
14362         if(records.length < 1) {
14363             
14364             // is this valid??  = should it render a template??
14365             
14366             this.el.update(this.emptyText);
14367             return;
14368         }
14369         var el = this.el;
14370         if (this.dataName) {
14371             this.el.update(t.apply(this.store.meta)); //????
14372             el = this.el.child('.roo-tpl-' + this.dataName);
14373         }
14374         
14375         for(var i = 0, len = records.length; i < len; i++){
14376             var data = this.prepareData(records[i].data, i, records[i]);
14377             this.fireEvent("preparedata", this, data, i, records[i]);
14378             
14379             var d = Roo.apply({}, data);
14380             
14381             if(this.tickable){
14382                 Roo.apply(d, {'roo-id' : Roo.id()});
14383                 
14384                 var _this = this;
14385             
14386                 Roo.each(this.parent.item, function(item){
14387                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14388                         return;
14389                     }
14390                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14391                 });
14392             }
14393             
14394             html[html.length] = Roo.util.Format.trim(
14395                 this.dataName ?
14396                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14397                     t.apply(d)
14398             );
14399         }
14400         
14401         
14402         
14403         el.update(html.join(""));
14404         this.nodes = el.dom.childNodes;
14405         this.updateIndexes(0);
14406     },
14407     
14408
14409     /**
14410      * Function to override to reformat the data that is sent to
14411      * the template for each node.
14412      * DEPRICATED - use the preparedata event handler.
14413      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14414      * a JSON object for an UpdateManager bound view).
14415      */
14416     prepareData : function(data, index, record)
14417     {
14418         this.fireEvent("preparedata", this, data, index, record);
14419         return data;
14420     },
14421
14422     onUpdate : function(ds, record){
14423         // Roo.log('on update');   
14424         this.clearSelections();
14425         var index = this.store.indexOf(record);
14426         var n = this.nodes[index];
14427         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14428         n.parentNode.removeChild(n);
14429         this.updateIndexes(index, index);
14430     },
14431
14432     
14433     
14434 // --------- FIXME     
14435     onAdd : function(ds, records, index)
14436     {
14437         //Roo.log(['on Add', ds, records, index] );        
14438         this.clearSelections();
14439         if(this.nodes.length == 0){
14440             this.refresh();
14441             return;
14442         }
14443         var n = this.nodes[index];
14444         for(var i = 0, len = records.length; i < len; i++){
14445             var d = this.prepareData(records[i].data, i, records[i]);
14446             if(n){
14447                 this.tpl.insertBefore(n, d);
14448             }else{
14449                 
14450                 this.tpl.append(this.el, d);
14451             }
14452         }
14453         this.updateIndexes(index);
14454     },
14455
14456     onRemove : function(ds, record, index){
14457        // Roo.log('onRemove');
14458         this.clearSelections();
14459         var el = this.dataName  ?
14460             this.el.child('.roo-tpl-' + this.dataName) :
14461             this.el; 
14462         
14463         el.dom.removeChild(this.nodes[index]);
14464         this.updateIndexes(index);
14465     },
14466
14467     /**
14468      * Refresh an individual node.
14469      * @param {Number} index
14470      */
14471     refreshNode : function(index){
14472         this.onUpdate(this.store, this.store.getAt(index));
14473     },
14474
14475     updateIndexes : function(startIndex, endIndex){
14476         var ns = this.nodes;
14477         startIndex = startIndex || 0;
14478         endIndex = endIndex || ns.length - 1;
14479         for(var i = startIndex; i <= endIndex; i++){
14480             ns[i].nodeIndex = i;
14481         }
14482     },
14483
14484     /**
14485      * Changes the data store this view uses and refresh the view.
14486      * @param {Store} store
14487      */
14488     setStore : function(store, initial){
14489         if(!initial && this.store){
14490             this.store.un("datachanged", this.refresh);
14491             this.store.un("add", this.onAdd);
14492             this.store.un("remove", this.onRemove);
14493             this.store.un("update", this.onUpdate);
14494             this.store.un("clear", this.refresh);
14495             this.store.un("beforeload", this.onBeforeLoad);
14496             this.store.un("load", this.onLoad);
14497             this.store.un("loadexception", this.onLoad);
14498         }
14499         if(store){
14500           
14501             store.on("datachanged", this.refresh, this);
14502             store.on("add", this.onAdd, this);
14503             store.on("remove", this.onRemove, this);
14504             store.on("update", this.onUpdate, this);
14505             store.on("clear", this.refresh, this);
14506             store.on("beforeload", this.onBeforeLoad, this);
14507             store.on("load", this.onLoad, this);
14508             store.on("loadexception", this.onLoad, this);
14509         }
14510         
14511         if(store){
14512             this.refresh();
14513         }
14514     },
14515     /**
14516      * onbeforeLoad - masks the loading area.
14517      *
14518      */
14519     onBeforeLoad : function(store,opts)
14520     {
14521          //Roo.log('onBeforeLoad');   
14522         if (!opts.add) {
14523             this.el.update("");
14524         }
14525         this.el.mask(this.mask ? this.mask : "Loading" ); 
14526     },
14527     onLoad : function ()
14528     {
14529         this.el.unmask();
14530     },
14531     
14532
14533     /**
14534      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14535      * @param {HTMLElement} node
14536      * @return {HTMLElement} The template node
14537      */
14538     findItemFromChild : function(node){
14539         var el = this.dataName  ?
14540             this.el.child('.roo-tpl-' + this.dataName,true) :
14541             this.el.dom; 
14542         
14543         if(!node || node.parentNode == el){
14544                     return node;
14545             }
14546             var p = node.parentNode;
14547             while(p && p != el){
14548             if(p.parentNode == el){
14549                 return p;
14550             }
14551             p = p.parentNode;
14552         }
14553             return null;
14554     },
14555
14556     /** @ignore */
14557     onClick : function(e){
14558         var item = this.findItemFromChild(e.getTarget());
14559         if(item){
14560             var index = this.indexOf(item);
14561             if(this.onItemClick(item, index, e) !== false){
14562                 this.fireEvent("click", this, index, item, e);
14563             }
14564         }else{
14565             this.clearSelections();
14566         }
14567     },
14568
14569     /** @ignore */
14570     onContextMenu : function(e){
14571         var item = this.findItemFromChild(e.getTarget());
14572         if(item){
14573             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14574         }
14575     },
14576
14577     /** @ignore */
14578     onDblClick : function(e){
14579         var item = this.findItemFromChild(e.getTarget());
14580         if(item){
14581             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14582         }
14583     },
14584
14585     onItemClick : function(item, index, e)
14586     {
14587         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14588             return false;
14589         }
14590         if (this.toggleSelect) {
14591             var m = this.isSelected(item) ? 'unselect' : 'select';
14592             //Roo.log(m);
14593             var _t = this;
14594             _t[m](item, true, false);
14595             return true;
14596         }
14597         if(this.multiSelect || this.singleSelect){
14598             if(this.multiSelect && e.shiftKey && this.lastSelection){
14599                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14600             }else{
14601                 this.select(item, this.multiSelect && e.ctrlKey);
14602                 this.lastSelection = item;
14603             }
14604             
14605             if(!this.tickable){
14606                 e.preventDefault();
14607             }
14608             
14609         }
14610         return true;
14611     },
14612
14613     /**
14614      * Get the number of selected nodes.
14615      * @return {Number}
14616      */
14617     getSelectionCount : function(){
14618         return this.selections.length;
14619     },
14620
14621     /**
14622      * Get the currently selected nodes.
14623      * @return {Array} An array of HTMLElements
14624      */
14625     getSelectedNodes : function(){
14626         return this.selections;
14627     },
14628
14629     /**
14630      * Get the indexes of the selected nodes.
14631      * @return {Array}
14632      */
14633     getSelectedIndexes : function(){
14634         var indexes = [], s = this.selections;
14635         for(var i = 0, len = s.length; i < len; i++){
14636             indexes.push(s[i].nodeIndex);
14637         }
14638         return indexes;
14639     },
14640
14641     /**
14642      * Clear all selections
14643      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14644      */
14645     clearSelections : function(suppressEvent){
14646         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14647             this.cmp.elements = this.selections;
14648             this.cmp.removeClass(this.selectedClass);
14649             this.selections = [];
14650             if(!suppressEvent){
14651                 this.fireEvent("selectionchange", this, this.selections);
14652             }
14653         }
14654     },
14655
14656     /**
14657      * Returns true if the passed node is selected
14658      * @param {HTMLElement/Number} node The node or node index
14659      * @return {Boolean}
14660      */
14661     isSelected : function(node){
14662         var s = this.selections;
14663         if(s.length < 1){
14664             return false;
14665         }
14666         node = this.getNode(node);
14667         return s.indexOf(node) !== -1;
14668     },
14669
14670     /**
14671      * Selects nodes.
14672      * @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
14673      * @param {Boolean} keepExisting (optional) true to keep existing selections
14674      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14675      */
14676     select : function(nodeInfo, keepExisting, suppressEvent){
14677         if(nodeInfo instanceof Array){
14678             if(!keepExisting){
14679                 this.clearSelections(true);
14680             }
14681             for(var i = 0, len = nodeInfo.length; i < len; i++){
14682                 this.select(nodeInfo[i], true, true);
14683             }
14684             return;
14685         } 
14686         var node = this.getNode(nodeInfo);
14687         if(!node || this.isSelected(node)){
14688             return; // already selected.
14689         }
14690         if(!keepExisting){
14691             this.clearSelections(true);
14692         }
14693         
14694         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14695             Roo.fly(node).addClass(this.selectedClass);
14696             this.selections.push(node);
14697             if(!suppressEvent){
14698                 this.fireEvent("selectionchange", this, this.selections);
14699             }
14700         }
14701         
14702         
14703     },
14704       /**
14705      * Unselects nodes.
14706      * @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
14707      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14708      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14709      */
14710     unselect : function(nodeInfo, keepExisting, suppressEvent)
14711     {
14712         if(nodeInfo instanceof Array){
14713             Roo.each(this.selections, function(s) {
14714                 this.unselect(s, nodeInfo);
14715             }, this);
14716             return;
14717         }
14718         var node = this.getNode(nodeInfo);
14719         if(!node || !this.isSelected(node)){
14720             //Roo.log("not selected");
14721             return; // not selected.
14722         }
14723         // fireevent???
14724         var ns = [];
14725         Roo.each(this.selections, function(s) {
14726             if (s == node ) {
14727                 Roo.fly(node).removeClass(this.selectedClass);
14728
14729                 return;
14730             }
14731             ns.push(s);
14732         },this);
14733         
14734         this.selections= ns;
14735         this.fireEvent("selectionchange", this, this.selections);
14736     },
14737
14738     /**
14739      * Gets a template node.
14740      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14741      * @return {HTMLElement} The node or null if it wasn't found
14742      */
14743     getNode : function(nodeInfo){
14744         if(typeof nodeInfo == "string"){
14745             return document.getElementById(nodeInfo);
14746         }else if(typeof nodeInfo == "number"){
14747             return this.nodes[nodeInfo];
14748         }
14749         return nodeInfo;
14750     },
14751
14752     /**
14753      * Gets a range template nodes.
14754      * @param {Number} startIndex
14755      * @param {Number} endIndex
14756      * @return {Array} An array of nodes
14757      */
14758     getNodes : function(start, end){
14759         var ns = this.nodes;
14760         start = start || 0;
14761         end = typeof end == "undefined" ? ns.length - 1 : end;
14762         var nodes = [];
14763         if(start <= end){
14764             for(var i = start; i <= end; i++){
14765                 nodes.push(ns[i]);
14766             }
14767         } else{
14768             for(var i = start; i >= end; i--){
14769                 nodes.push(ns[i]);
14770             }
14771         }
14772         return nodes;
14773     },
14774
14775     /**
14776      * Finds the index of the passed node
14777      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14778      * @return {Number} The index of the node or -1
14779      */
14780     indexOf : function(node){
14781         node = this.getNode(node);
14782         if(typeof node.nodeIndex == "number"){
14783             return node.nodeIndex;
14784         }
14785         var ns = this.nodes;
14786         for(var i = 0, len = ns.length; i < len; i++){
14787             if(ns[i] == node){
14788                 return i;
14789             }
14790         }
14791         return -1;
14792     }
14793 });
14794 /*
14795  * - LGPL
14796  *
14797  * based on jquery fullcalendar
14798  * 
14799  */
14800
14801 Roo.bootstrap = Roo.bootstrap || {};
14802 /**
14803  * @class Roo.bootstrap.Calendar
14804  * @extends Roo.bootstrap.Component
14805  * Bootstrap Calendar class
14806  * @cfg {Boolean} loadMask (true|false) default false
14807  * @cfg {Object} header generate the user specific header of the calendar, default false
14808
14809  * @constructor
14810  * Create a new Container
14811  * @param {Object} config The config object
14812  */
14813
14814
14815
14816 Roo.bootstrap.Calendar = function(config){
14817     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14818      this.addEvents({
14819         /**
14820              * @event select
14821              * Fires when a date is selected
14822              * @param {DatePicker} this
14823              * @param {Date} date The selected date
14824              */
14825         'select': true,
14826         /**
14827              * @event monthchange
14828              * Fires when the displayed month changes 
14829              * @param {DatePicker} this
14830              * @param {Date} date The selected month
14831              */
14832         'monthchange': true,
14833         /**
14834              * @event evententer
14835              * Fires when mouse over an event
14836              * @param {Calendar} this
14837              * @param {event} Event
14838              */
14839         'evententer': true,
14840         /**
14841              * @event eventleave
14842              * Fires when the mouse leaves an
14843              * @param {Calendar} this
14844              * @param {event}
14845              */
14846         'eventleave': true,
14847         /**
14848              * @event eventclick
14849              * Fires when the mouse click an
14850              * @param {Calendar} this
14851              * @param {event}
14852              */
14853         'eventclick': true
14854         
14855     });
14856
14857 };
14858
14859 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14860     
14861      /**
14862      * @cfg {Number} startDay
14863      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14864      */
14865     startDay : 0,
14866     
14867     loadMask : false,
14868     
14869     header : false,
14870       
14871     getAutoCreate : function(){
14872         
14873         
14874         var fc_button = function(name, corner, style, content ) {
14875             return Roo.apply({},{
14876                 tag : 'span',
14877                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14878                          (corner.length ?
14879                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14880                             ''
14881                         ),
14882                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14883                 unselectable: 'on'
14884             });
14885         };
14886         
14887         var header = {};
14888         
14889         if(!this.header){
14890             header = {
14891                 tag : 'table',
14892                 cls : 'fc-header',
14893                 style : 'width:100%',
14894                 cn : [
14895                     {
14896                         tag: 'tr',
14897                         cn : [
14898                             {
14899                                 tag : 'td',
14900                                 cls : 'fc-header-left',
14901                                 cn : [
14902                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14903                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14904                                     { tag: 'span', cls: 'fc-header-space' },
14905                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14906
14907
14908                                 ]
14909                             },
14910
14911                             {
14912                                 tag : 'td',
14913                                 cls : 'fc-header-center',
14914                                 cn : [
14915                                     {
14916                                         tag: 'span',
14917                                         cls: 'fc-header-title',
14918                                         cn : {
14919                                             tag: 'H2',
14920                                             html : 'month / year'
14921                                         }
14922                                     }
14923
14924                                 ]
14925                             },
14926                             {
14927                                 tag : 'td',
14928                                 cls : 'fc-header-right',
14929                                 cn : [
14930                               /*      fc_button('month', 'left', '', 'month' ),
14931                                     fc_button('week', '', '', 'week' ),
14932                                     fc_button('day', 'right', '', 'day' )
14933                                 */    
14934
14935                                 ]
14936                             }
14937
14938                         ]
14939                     }
14940                 ]
14941             };
14942         }
14943         
14944         header = this.header;
14945         
14946        
14947         var cal_heads = function() {
14948             var ret = [];
14949             // fixme - handle this.
14950             
14951             for (var i =0; i < Date.dayNames.length; i++) {
14952                 var d = Date.dayNames[i];
14953                 ret.push({
14954                     tag: 'th',
14955                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14956                     html : d.substring(0,3)
14957                 });
14958                 
14959             }
14960             ret[0].cls += ' fc-first';
14961             ret[6].cls += ' fc-last';
14962             return ret;
14963         };
14964         var cal_cell = function(n) {
14965             return  {
14966                 tag: 'td',
14967                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14968                 cn : [
14969                     {
14970                         cn : [
14971                             {
14972                                 cls: 'fc-day-number',
14973                                 html: 'D'
14974                             },
14975                             {
14976                                 cls: 'fc-day-content',
14977                              
14978                                 cn : [
14979                                      {
14980                                         style: 'position: relative;' // height: 17px;
14981                                     }
14982                                 ]
14983                             }
14984                             
14985                             
14986                         ]
14987                     }
14988                 ]
14989                 
14990             }
14991         };
14992         var cal_rows = function() {
14993             
14994             var ret = [];
14995             for (var r = 0; r < 6; r++) {
14996                 var row= {
14997                     tag : 'tr',
14998                     cls : 'fc-week',
14999                     cn : []
15000                 };
15001                 
15002                 for (var i =0; i < Date.dayNames.length; i++) {
15003                     var d = Date.dayNames[i];
15004                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15005
15006                 }
15007                 row.cn[0].cls+=' fc-first';
15008                 row.cn[0].cn[0].style = 'min-height:90px';
15009                 row.cn[6].cls+=' fc-last';
15010                 ret.push(row);
15011                 
15012             }
15013             ret[0].cls += ' fc-first';
15014             ret[4].cls += ' fc-prev-last';
15015             ret[5].cls += ' fc-last';
15016             return ret;
15017             
15018         };
15019         
15020         var cal_table = {
15021             tag: 'table',
15022             cls: 'fc-border-separate',
15023             style : 'width:100%',
15024             cellspacing  : 0,
15025             cn : [
15026                 { 
15027                     tag: 'thead',
15028                     cn : [
15029                         { 
15030                             tag: 'tr',
15031                             cls : 'fc-first fc-last',
15032                             cn : cal_heads()
15033                         }
15034                     ]
15035                 },
15036                 { 
15037                     tag: 'tbody',
15038                     cn : cal_rows()
15039                 }
15040                   
15041             ]
15042         };
15043          
15044          var cfg = {
15045             cls : 'fc fc-ltr',
15046             cn : [
15047                 header,
15048                 {
15049                     cls : 'fc-content',
15050                     style : "position: relative;",
15051                     cn : [
15052                         {
15053                             cls : 'fc-view fc-view-month fc-grid',
15054                             style : 'position: relative',
15055                             unselectable : 'on',
15056                             cn : [
15057                                 {
15058                                     cls : 'fc-event-container',
15059                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15060                                 },
15061                                 cal_table
15062                             ]
15063                         }
15064                     ]
15065     
15066                 }
15067            ] 
15068             
15069         };
15070         
15071          
15072         
15073         return cfg;
15074     },
15075     
15076     
15077     initEvents : function()
15078     {
15079         if(!this.store){
15080             throw "can not find store for calendar";
15081         }
15082         
15083         var mark = {
15084             tag: "div",
15085             cls:"x-dlg-mask",
15086             style: "text-align:center",
15087             cn: [
15088                 {
15089                     tag: "div",
15090                     style: "background-color:white;width:50%;margin:250 auto",
15091                     cn: [
15092                         {
15093                             tag: "img",
15094                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15095                         },
15096                         {
15097                             tag: "span",
15098                             html: "Loading"
15099                         }
15100                         
15101                     ]
15102                 }
15103             ]
15104         };
15105         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15106         
15107         var size = this.el.select('.fc-content', true).first().getSize();
15108         this.maskEl.setSize(size.width, size.height);
15109         this.maskEl.enableDisplayMode("block");
15110         if(!this.loadMask){
15111             this.maskEl.hide();
15112         }
15113         
15114         this.store = Roo.factory(this.store, Roo.data);
15115         this.store.on('load', this.onLoad, this);
15116         this.store.on('beforeload', this.onBeforeLoad, this);
15117         
15118         this.resize();
15119         
15120         this.cells = this.el.select('.fc-day',true);
15121         //Roo.log(this.cells);
15122         this.textNodes = this.el.query('.fc-day-number');
15123         this.cells.addClassOnOver('fc-state-hover');
15124         
15125         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15126         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15127         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15128         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15129         
15130         this.on('monthchange', this.onMonthChange, this);
15131         
15132         this.update(new Date().clearTime());
15133     },
15134     
15135     resize : function() {
15136         var sz  = this.el.getSize();
15137         
15138         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15139         this.el.select('.fc-day-content div',true).setHeight(34);
15140     },
15141     
15142     
15143     // private
15144     showPrevMonth : function(e){
15145         this.update(this.activeDate.add("mo", -1));
15146     },
15147     showToday : function(e){
15148         this.update(new Date().clearTime());
15149     },
15150     // private
15151     showNextMonth : function(e){
15152         this.update(this.activeDate.add("mo", 1));
15153     },
15154
15155     // private
15156     showPrevYear : function(){
15157         this.update(this.activeDate.add("y", -1));
15158     },
15159
15160     // private
15161     showNextYear : function(){
15162         this.update(this.activeDate.add("y", 1));
15163     },
15164
15165     
15166    // private
15167     update : function(date)
15168     {
15169         var vd = this.activeDate;
15170         this.activeDate = date;
15171 //        if(vd && this.el){
15172 //            var t = date.getTime();
15173 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15174 //                Roo.log('using add remove');
15175 //                
15176 //                this.fireEvent('monthchange', this, date);
15177 //                
15178 //                this.cells.removeClass("fc-state-highlight");
15179 //                this.cells.each(function(c){
15180 //                   if(c.dateValue == t){
15181 //                       c.addClass("fc-state-highlight");
15182 //                       setTimeout(function(){
15183 //                            try{c.dom.firstChild.focus();}catch(e){}
15184 //                       }, 50);
15185 //                       return false;
15186 //                   }
15187 //                   return true;
15188 //                });
15189 //                return;
15190 //            }
15191 //        }
15192         
15193         var days = date.getDaysInMonth();
15194         
15195         var firstOfMonth = date.getFirstDateOfMonth();
15196         var startingPos = firstOfMonth.getDay()-this.startDay;
15197         
15198         if(startingPos < this.startDay){
15199             startingPos += 7;
15200         }
15201         
15202         var pm = date.add(Date.MONTH, -1);
15203         var prevStart = pm.getDaysInMonth()-startingPos;
15204 //        
15205         this.cells = this.el.select('.fc-day',true);
15206         this.textNodes = this.el.query('.fc-day-number');
15207         this.cells.addClassOnOver('fc-state-hover');
15208         
15209         var cells = this.cells.elements;
15210         var textEls = this.textNodes;
15211         
15212         Roo.each(cells, function(cell){
15213             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15214         });
15215         
15216         days += startingPos;
15217
15218         // convert everything to numbers so it's fast
15219         var day = 86400000;
15220         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15221         //Roo.log(d);
15222         //Roo.log(pm);
15223         //Roo.log(prevStart);
15224         
15225         var today = new Date().clearTime().getTime();
15226         var sel = date.clearTime().getTime();
15227         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15228         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15229         var ddMatch = this.disabledDatesRE;
15230         var ddText = this.disabledDatesText;
15231         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15232         var ddaysText = this.disabledDaysText;
15233         var format = this.format;
15234         
15235         var setCellClass = function(cal, cell){
15236             cell.row = 0;
15237             cell.events = [];
15238             cell.more = [];
15239             //Roo.log('set Cell Class');
15240             cell.title = "";
15241             var t = d.getTime();
15242             
15243             //Roo.log(d);
15244             
15245             cell.dateValue = t;
15246             if(t == today){
15247                 cell.className += " fc-today";
15248                 cell.className += " fc-state-highlight";
15249                 cell.title = cal.todayText;
15250             }
15251             if(t == sel){
15252                 // disable highlight in other month..
15253                 //cell.className += " fc-state-highlight";
15254                 
15255             }
15256             // disabling
15257             if(t < min) {
15258                 cell.className = " fc-state-disabled";
15259                 cell.title = cal.minText;
15260                 return;
15261             }
15262             if(t > max) {
15263                 cell.className = " fc-state-disabled";
15264                 cell.title = cal.maxText;
15265                 return;
15266             }
15267             if(ddays){
15268                 if(ddays.indexOf(d.getDay()) != -1){
15269                     cell.title = ddaysText;
15270                     cell.className = " fc-state-disabled";
15271                 }
15272             }
15273             if(ddMatch && format){
15274                 var fvalue = d.dateFormat(format);
15275                 if(ddMatch.test(fvalue)){
15276                     cell.title = ddText.replace("%0", fvalue);
15277                     cell.className = " fc-state-disabled";
15278                 }
15279             }
15280             
15281             if (!cell.initialClassName) {
15282                 cell.initialClassName = cell.dom.className;
15283             }
15284             
15285             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15286         };
15287
15288         var i = 0;
15289         
15290         for(; i < startingPos; i++) {
15291             textEls[i].innerHTML = (++prevStart);
15292             d.setDate(d.getDate()+1);
15293             
15294             cells[i].className = "fc-past fc-other-month";
15295             setCellClass(this, cells[i]);
15296         }
15297         
15298         var intDay = 0;
15299         
15300         for(; i < days; i++){
15301             intDay = i - startingPos + 1;
15302             textEls[i].innerHTML = (intDay);
15303             d.setDate(d.getDate()+1);
15304             
15305             cells[i].className = ''; // "x-date-active";
15306             setCellClass(this, cells[i]);
15307         }
15308         var extraDays = 0;
15309         
15310         for(; i < 42; i++) {
15311             textEls[i].innerHTML = (++extraDays);
15312             d.setDate(d.getDate()+1);
15313             
15314             cells[i].className = "fc-future fc-other-month";
15315             setCellClass(this, cells[i]);
15316         }
15317         
15318         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15319         
15320         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15321         
15322         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15323         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15324         
15325         if(totalRows != 6){
15326             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15327             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15328         }
15329         
15330         this.fireEvent('monthchange', this, date);
15331         
15332         
15333         /*
15334         if(!this.internalRender){
15335             var main = this.el.dom.firstChild;
15336             var w = main.offsetWidth;
15337             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15338             Roo.fly(main).setWidth(w);
15339             this.internalRender = true;
15340             // opera does not respect the auto grow header center column
15341             // then, after it gets a width opera refuses to recalculate
15342             // without a second pass
15343             if(Roo.isOpera && !this.secondPass){
15344                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15345                 this.secondPass = true;
15346                 this.update.defer(10, this, [date]);
15347             }
15348         }
15349         */
15350         
15351     },
15352     
15353     findCell : function(dt) {
15354         dt = dt.clearTime().getTime();
15355         var ret = false;
15356         this.cells.each(function(c){
15357             //Roo.log("check " +c.dateValue + '?=' + dt);
15358             if(c.dateValue == dt){
15359                 ret = c;
15360                 return false;
15361             }
15362             return true;
15363         });
15364         
15365         return ret;
15366     },
15367     
15368     findCells : function(ev) {
15369         var s = ev.start.clone().clearTime().getTime();
15370        // Roo.log(s);
15371         var e= ev.end.clone().clearTime().getTime();
15372        // Roo.log(e);
15373         var ret = [];
15374         this.cells.each(function(c){
15375              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15376             
15377             if(c.dateValue > e){
15378                 return ;
15379             }
15380             if(c.dateValue < s){
15381                 return ;
15382             }
15383             ret.push(c);
15384         });
15385         
15386         return ret;    
15387     },
15388     
15389 //    findBestRow: function(cells)
15390 //    {
15391 //        var ret = 0;
15392 //        
15393 //        for (var i =0 ; i < cells.length;i++) {
15394 //            ret  = Math.max(cells[i].rows || 0,ret);
15395 //        }
15396 //        return ret;
15397 //        
15398 //    },
15399     
15400     
15401     addItem : function(ev)
15402     {
15403         // look for vertical location slot in
15404         var cells = this.findCells(ev);
15405         
15406 //        ev.row = this.findBestRow(cells);
15407         
15408         // work out the location.
15409         
15410         var crow = false;
15411         var rows = [];
15412         for(var i =0; i < cells.length; i++) {
15413             
15414             cells[i].row = cells[0].row;
15415             
15416             if(i == 0){
15417                 cells[i].row = cells[i].row + 1;
15418             }
15419             
15420             if (!crow) {
15421                 crow = {
15422                     start : cells[i],
15423                     end :  cells[i]
15424                 };
15425                 continue;
15426             }
15427             if (crow.start.getY() == cells[i].getY()) {
15428                 // on same row.
15429                 crow.end = cells[i];
15430                 continue;
15431             }
15432             // different row.
15433             rows.push(crow);
15434             crow = {
15435                 start: cells[i],
15436                 end : cells[i]
15437             };
15438             
15439         }
15440         
15441         rows.push(crow);
15442         ev.els = [];
15443         ev.rows = rows;
15444         ev.cells = cells;
15445         
15446         cells[0].events.push(ev);
15447         
15448         this.calevents.push(ev);
15449     },
15450     
15451     clearEvents: function() {
15452         
15453         if(!this.calevents){
15454             return;
15455         }
15456         
15457         Roo.each(this.cells.elements, function(c){
15458             c.row = 0;
15459             c.events = [];
15460             c.more = [];
15461         });
15462         
15463         Roo.each(this.calevents, function(e) {
15464             Roo.each(e.els, function(el) {
15465                 el.un('mouseenter' ,this.onEventEnter, this);
15466                 el.un('mouseleave' ,this.onEventLeave, this);
15467                 el.remove();
15468             },this);
15469         },this);
15470         
15471         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15472             e.remove();
15473         });
15474         
15475     },
15476     
15477     renderEvents: function()
15478     {   
15479         var _this = this;
15480         
15481         this.cells.each(function(c) {
15482             
15483             if(c.row < 5){
15484                 return;
15485             }
15486             
15487             var ev = c.events;
15488             
15489             var r = 4;
15490             if(c.row != c.events.length){
15491                 r = 4 - (4 - (c.row - c.events.length));
15492             }
15493             
15494             c.events = ev.slice(0, r);
15495             c.more = ev.slice(r);
15496             
15497             if(c.more.length && c.more.length == 1){
15498                 c.events.push(c.more.pop());
15499             }
15500             
15501             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15502             
15503         });
15504             
15505         this.cells.each(function(c) {
15506             
15507             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15508             
15509             
15510             for (var e = 0; e < c.events.length; e++){
15511                 var ev = c.events[e];
15512                 var rows = ev.rows;
15513                 
15514                 for(var i = 0; i < rows.length; i++) {
15515                 
15516                     // how many rows should it span..
15517
15518                     var  cfg = {
15519                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15520                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15521
15522                         unselectable : "on",
15523                         cn : [
15524                             {
15525                                 cls: 'fc-event-inner',
15526                                 cn : [
15527     //                                {
15528     //                                  tag:'span',
15529     //                                  cls: 'fc-event-time',
15530     //                                  html : cells.length > 1 ? '' : ev.time
15531     //                                },
15532                                     {
15533                                       tag:'span',
15534                                       cls: 'fc-event-title',
15535                                       html : String.format('{0}', ev.title)
15536                                     }
15537
15538
15539                                 ]
15540                             },
15541                             {
15542                                 cls: 'ui-resizable-handle ui-resizable-e',
15543                                 html : '&nbsp;&nbsp;&nbsp'
15544                             }
15545
15546                         ]
15547                     };
15548
15549                     if (i == 0) {
15550                         cfg.cls += ' fc-event-start';
15551                     }
15552                     if ((i+1) == rows.length) {
15553                         cfg.cls += ' fc-event-end';
15554                     }
15555
15556                     var ctr = _this.el.select('.fc-event-container',true).first();
15557                     var cg = ctr.createChild(cfg);
15558
15559                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15560                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15561
15562                     var r = (c.more.length) ? 1 : 0;
15563                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15564                     cg.setWidth(ebox.right - sbox.x -2);
15565
15566                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15567                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15568                     cg.on('click', _this.onEventClick, _this, ev);
15569
15570                     ev.els.push(cg);
15571                     
15572                 }
15573                 
15574             }
15575             
15576             
15577             if(c.more.length){
15578                 var  cfg = {
15579                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15580                     style : 'position: absolute',
15581                     unselectable : "on",
15582                     cn : [
15583                         {
15584                             cls: 'fc-event-inner',
15585                             cn : [
15586                                 {
15587                                   tag:'span',
15588                                   cls: 'fc-event-title',
15589                                   html : 'More'
15590                                 }
15591
15592
15593                             ]
15594                         },
15595                         {
15596                             cls: 'ui-resizable-handle ui-resizable-e',
15597                             html : '&nbsp;&nbsp;&nbsp'
15598                         }
15599
15600                     ]
15601                 };
15602
15603                 var ctr = _this.el.select('.fc-event-container',true).first();
15604                 var cg = ctr.createChild(cfg);
15605
15606                 var sbox = c.select('.fc-day-content',true).first().getBox();
15607                 var ebox = c.select('.fc-day-content',true).first().getBox();
15608                 //Roo.log(cg);
15609                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15610                 cg.setWidth(ebox.right - sbox.x -2);
15611
15612                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15613                 
15614             }
15615             
15616         });
15617         
15618         
15619         
15620     },
15621     
15622     onEventEnter: function (e, el,event,d) {
15623         this.fireEvent('evententer', this, el, event);
15624     },
15625     
15626     onEventLeave: function (e, el,event,d) {
15627         this.fireEvent('eventleave', this, el, event);
15628     },
15629     
15630     onEventClick: function (e, el,event,d) {
15631         this.fireEvent('eventclick', this, el, event);
15632     },
15633     
15634     onMonthChange: function () {
15635         this.store.load();
15636     },
15637     
15638     onMoreEventClick: function(e, el, more)
15639     {
15640         var _this = this;
15641         
15642         this.calpopover.placement = 'right';
15643         this.calpopover.setTitle('More');
15644         
15645         this.calpopover.setContent('');
15646         
15647         var ctr = this.calpopover.el.select('.popover-content', true).first();
15648         
15649         Roo.each(more, function(m){
15650             var cfg = {
15651                 cls : 'fc-event-hori fc-event-draggable',
15652                 html : m.title
15653             };
15654             var cg = ctr.createChild(cfg);
15655             
15656             cg.on('click', _this.onEventClick, _this, m);
15657         });
15658         
15659         this.calpopover.show(el);
15660         
15661         
15662     },
15663     
15664     onLoad: function () 
15665     {   
15666         this.calevents = [];
15667         var cal = this;
15668         
15669         if(this.store.getCount() > 0){
15670             this.store.data.each(function(d){
15671                cal.addItem({
15672                     id : d.data.id,
15673                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15674                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15675                     time : d.data.start_time,
15676                     title : d.data.title,
15677                     description : d.data.description,
15678                     venue : d.data.venue
15679                 });
15680             });
15681         }
15682         
15683         this.renderEvents();
15684         
15685         if(this.calevents.length && this.loadMask){
15686             this.maskEl.hide();
15687         }
15688     },
15689     
15690     onBeforeLoad: function()
15691     {
15692         this.clearEvents();
15693         if(this.loadMask){
15694             this.maskEl.show();
15695         }
15696     }
15697 });
15698
15699  
15700  /*
15701  * - LGPL
15702  *
15703  * element
15704  * 
15705  */
15706
15707 /**
15708  * @class Roo.bootstrap.Popover
15709  * @extends Roo.bootstrap.Component
15710  * Bootstrap Popover class
15711  * @cfg {String} html contents of the popover   (or false to use children..)
15712  * @cfg {String} title of popover (or false to hide)
15713  * @cfg {String} placement how it is placed
15714  * @cfg {String} trigger click || hover (or false to trigger manually)
15715  * @cfg {String} over what (parent or false to trigger manually.)
15716  * @cfg {Number} delay - delay before showing
15717  
15718  * @constructor
15719  * Create a new Popover
15720  * @param {Object} config The config object
15721  */
15722
15723 Roo.bootstrap.Popover = function(config){
15724     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15725     
15726     this.addEvents({
15727         // raw events
15728          /**
15729          * @event show
15730          * After the popover show
15731          * 
15732          * @param {Roo.bootstrap.Popover} this
15733          */
15734         "show" : true,
15735         /**
15736          * @event hide
15737          * After the popover hide
15738          * 
15739          * @param {Roo.bootstrap.Popover} this
15740          */
15741         "hide" : true
15742     });
15743 };
15744
15745 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15746     
15747     title: 'Fill in a title',
15748     html: false,
15749     
15750     placement : 'right',
15751     trigger : 'hover', // hover
15752     
15753     delay : 0,
15754     
15755     over: 'parent',
15756     
15757     can_build_overlaid : false,
15758     
15759     getChildContainer : function()
15760     {
15761         return this.el.select('.popover-content',true).first();
15762     },
15763     
15764     getAutoCreate : function(){
15765          
15766         var cfg = {
15767            cls : 'popover roo-dynamic',
15768            style: 'display:block',
15769            cn : [
15770                 {
15771                     cls : 'arrow'
15772                 },
15773                 {
15774                     cls : 'popover-inner',
15775                     cn : [
15776                         {
15777                             tag: 'h3',
15778                             cls: 'popover-title',
15779                             html : this.title
15780                         },
15781                         {
15782                             cls : 'popover-content',
15783                             html : this.html
15784                         }
15785                     ]
15786                     
15787                 }
15788            ]
15789         };
15790         
15791         return cfg;
15792     },
15793     setTitle: function(str)
15794     {
15795         this.title = str;
15796         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15797     },
15798     setContent: function(str)
15799     {
15800         this.html = str;
15801         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15802     },
15803     // as it get's added to the bottom of the page.
15804     onRender : function(ct, position)
15805     {
15806         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15807         if(!this.el){
15808             var cfg = Roo.apply({},  this.getAutoCreate());
15809             cfg.id = Roo.id();
15810             
15811             if (this.cls) {
15812                 cfg.cls += ' ' + this.cls;
15813             }
15814             if (this.style) {
15815                 cfg.style = this.style;
15816             }
15817             //Roo.log("adding to ");
15818             this.el = Roo.get(document.body).createChild(cfg, position);
15819 //            Roo.log(this.el);
15820         }
15821         this.initEvents();
15822     },
15823     
15824     initEvents : function()
15825     {
15826         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15827         this.el.enableDisplayMode('block');
15828         this.el.hide();
15829         if (this.over === false) {
15830             return; 
15831         }
15832         if (this.triggers === false) {
15833             return;
15834         }
15835         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15836         var triggers = this.trigger ? this.trigger.split(' ') : [];
15837         Roo.each(triggers, function(trigger) {
15838         
15839             if (trigger == 'click') {
15840                 on_el.on('click', this.toggle, this);
15841             } else if (trigger != 'manual') {
15842                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15843                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15844       
15845                 on_el.on(eventIn  ,this.enter, this);
15846                 on_el.on(eventOut, this.leave, this);
15847             }
15848         }, this);
15849         
15850     },
15851     
15852     
15853     // private
15854     timeout : null,
15855     hoverState : null,
15856     
15857     toggle : function () {
15858         this.hoverState == 'in' ? this.leave() : this.enter();
15859     },
15860     
15861     enter : function () {
15862        
15863     
15864         clearTimeout(this.timeout);
15865     
15866         this.hoverState = 'in';
15867     
15868         if (!this.delay || !this.delay.show) {
15869             this.show();
15870             return;
15871         }
15872         var _t = this;
15873         this.timeout = setTimeout(function () {
15874             if (_t.hoverState == 'in') {
15875                 _t.show();
15876             }
15877         }, this.delay.show)
15878     },
15879     leave : function() {
15880         clearTimeout(this.timeout);
15881     
15882         this.hoverState = 'out';
15883     
15884         if (!this.delay || !this.delay.hide) {
15885             this.hide();
15886             return;
15887         }
15888         var _t = this;
15889         this.timeout = setTimeout(function () {
15890             if (_t.hoverState == 'out') {
15891                 _t.hide();
15892             }
15893         }, this.delay.hide)
15894     },
15895     
15896     show : function (on_el)
15897     {
15898         if (!on_el) {
15899             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15900         }
15901         // set content.
15902         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15903         if (this.html !== false) {
15904             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15905         }
15906         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15907         if (!this.title.length) {
15908             this.el.select('.popover-title',true).hide();
15909         }
15910         
15911         var placement = typeof this.placement == 'function' ?
15912             this.placement.call(this, this.el, on_el) :
15913             this.placement;
15914             
15915         var autoToken = /\s?auto?\s?/i;
15916         var autoPlace = autoToken.test(placement);
15917         if (autoPlace) {
15918             placement = placement.replace(autoToken, '') || 'top';
15919         }
15920         
15921         //this.el.detach()
15922         //this.el.setXY([0,0]);
15923         this.el.show();
15924         this.el.dom.style.display='block';
15925         this.el.addClass(placement);
15926         
15927         //this.el.appendTo(on_el);
15928         
15929         var p = this.getPosition();
15930         var box = this.el.getBox();
15931         
15932         if (autoPlace) {
15933             // fixme..
15934         }
15935         var align = Roo.bootstrap.Popover.alignment[placement];
15936         this.el.alignTo(on_el, align[0],align[1]);
15937         //var arrow = this.el.select('.arrow',true).first();
15938         //arrow.set(align[2], 
15939         
15940         this.el.addClass('in');
15941         
15942         
15943         if (this.el.hasClass('fade')) {
15944             // fade it?
15945         }
15946         
15947         this.fireEvent('show', this);
15948         
15949     },
15950     hide : function()
15951     {
15952         this.el.setXY([0,0]);
15953         this.el.removeClass('in');
15954         this.el.hide();
15955         this.hoverState = null;
15956         
15957         this.fireEvent('hide', this);
15958     }
15959     
15960 });
15961
15962 Roo.bootstrap.Popover.alignment = {
15963     'left' : ['r-l', [-10,0], 'right'],
15964     'right' : ['l-r', [10,0], 'left'],
15965     'bottom' : ['t-b', [0,10], 'top'],
15966     'top' : [ 'b-t', [0,-10], 'bottom']
15967 };
15968
15969  /*
15970  * - LGPL
15971  *
15972  * Progress
15973  * 
15974  */
15975
15976 /**
15977  * @class Roo.bootstrap.Progress
15978  * @extends Roo.bootstrap.Component
15979  * Bootstrap Progress class
15980  * @cfg {Boolean} striped striped of the progress bar
15981  * @cfg {Boolean} active animated of the progress bar
15982  * 
15983  * 
15984  * @constructor
15985  * Create a new Progress
15986  * @param {Object} config The config object
15987  */
15988
15989 Roo.bootstrap.Progress = function(config){
15990     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15991 };
15992
15993 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15994     
15995     striped : false,
15996     active: false,
15997     
15998     getAutoCreate : function(){
15999         var cfg = {
16000             tag: 'div',
16001             cls: 'progress'
16002         };
16003         
16004         
16005         if(this.striped){
16006             cfg.cls += ' progress-striped';
16007         }
16008       
16009         if(this.active){
16010             cfg.cls += ' active';
16011         }
16012         
16013         
16014         return cfg;
16015     }
16016    
16017 });
16018
16019  
16020
16021  /*
16022  * - LGPL
16023  *
16024  * ProgressBar
16025  * 
16026  */
16027
16028 /**
16029  * @class Roo.bootstrap.ProgressBar
16030  * @extends Roo.bootstrap.Component
16031  * Bootstrap ProgressBar class
16032  * @cfg {Number} aria_valuenow aria-value now
16033  * @cfg {Number} aria_valuemin aria-value min
16034  * @cfg {Number} aria_valuemax aria-value max
16035  * @cfg {String} label label for the progress bar
16036  * @cfg {String} panel (success | info | warning | danger )
16037  * @cfg {String} role role of the progress bar
16038  * @cfg {String} sr_only text
16039  * 
16040  * 
16041  * @constructor
16042  * Create a new ProgressBar
16043  * @param {Object} config The config object
16044  */
16045
16046 Roo.bootstrap.ProgressBar = function(config){
16047     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16048 };
16049
16050 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16051     
16052     aria_valuenow : 0,
16053     aria_valuemin : 0,
16054     aria_valuemax : 100,
16055     label : false,
16056     panel : false,
16057     role : false,
16058     sr_only: false,
16059     
16060     getAutoCreate : function()
16061     {
16062         
16063         var cfg = {
16064             tag: 'div',
16065             cls: 'progress-bar',
16066             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16067         };
16068         
16069         if(this.sr_only){
16070             cfg.cn = {
16071                 tag: 'span',
16072                 cls: 'sr-only',
16073                 html: this.sr_only
16074             }
16075         }
16076         
16077         if(this.role){
16078             cfg.role = this.role;
16079         }
16080         
16081         if(this.aria_valuenow){
16082             cfg['aria-valuenow'] = this.aria_valuenow;
16083         }
16084         
16085         if(this.aria_valuemin){
16086             cfg['aria-valuemin'] = this.aria_valuemin;
16087         }
16088         
16089         if(this.aria_valuemax){
16090             cfg['aria-valuemax'] = this.aria_valuemax;
16091         }
16092         
16093         if(this.label && !this.sr_only){
16094             cfg.html = this.label;
16095         }
16096         
16097         if(this.panel){
16098             cfg.cls += ' progress-bar-' + this.panel;
16099         }
16100         
16101         return cfg;
16102     },
16103     
16104     update : function(aria_valuenow)
16105     {
16106         this.aria_valuenow = aria_valuenow;
16107         
16108         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16109     }
16110    
16111 });
16112
16113  
16114
16115  /*
16116  * - LGPL
16117  *
16118  * column
16119  * 
16120  */
16121
16122 /**
16123  * @class Roo.bootstrap.TabGroup
16124  * @extends Roo.bootstrap.Column
16125  * Bootstrap Column class
16126  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16127  * @cfg {Boolean} carousel true to make the group behave like a carousel
16128  * @cfg {Boolean} bullets show bullets for the panels
16129  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16130  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16131  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16132  * 
16133  * @constructor
16134  * Create a new TabGroup
16135  * @param {Object} config The config object
16136  */
16137
16138 Roo.bootstrap.TabGroup = function(config){
16139     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16140     if (!this.navId) {
16141         this.navId = Roo.id();
16142     }
16143     this.tabs = [];
16144     Roo.bootstrap.TabGroup.register(this);
16145     
16146 };
16147
16148 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16149     
16150     carousel : false,
16151     transition : false,
16152     bullets : 0,
16153     timer : 0,
16154     autoslide : false,
16155     slideFn : false,
16156     slideOnTouch : false,
16157     
16158     getAutoCreate : function()
16159     {
16160         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16161         
16162         cfg.cls += ' tab-content';
16163         
16164         if (this.carousel) {
16165             cfg.cls += ' carousel slide';
16166             
16167             cfg.cn = [{
16168                cls : 'carousel-inner'
16169             }];
16170         
16171             if(this.bullets  && !Roo.isTouch){
16172                 
16173                 var bullets = {
16174                     cls : 'carousel-bullets',
16175                     cn : []
16176                 };
16177                
16178                 if(this.bullets_cls){
16179                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16180                 }
16181                  /*
16182                 for (var i = 0; i < this.bullets; i++){
16183                     bullets.cn.push({
16184                         cls : 'bullet bullet-' + i
16185                     });
16186                 }
16187                 */
16188                 bullets.cn.push({
16189                     cls : 'clear'
16190                 });
16191                 
16192                 cfg.cn[0].cn = bullets;
16193             }
16194         }
16195         
16196         return cfg;
16197     },
16198     
16199     initEvents:  function()
16200     {
16201         if(Roo.isTouch && this.slideOnTouch){
16202             this.el.on("touchstart", this.onTouchStart, this);
16203         }
16204         
16205         if(this.autoslide){
16206             var _this = this;
16207             
16208             this.slideFn = window.setInterval(function() {
16209                 _this.showPanelNext();
16210             }, this.timer);
16211         }
16212         
16213     },
16214     
16215     onTouchStart : function(e, el, o)
16216     {
16217         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16218             return;
16219         }
16220         
16221         this.showPanelNext();
16222     },
16223     
16224     getChildContainer : function()
16225     {
16226         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16227     },
16228     
16229     /**
16230     * register a Navigation item
16231     * @param {Roo.bootstrap.NavItem} the navitem to add
16232     */
16233     register : function(item)
16234     {
16235         this.tabs.push( item);
16236         item.navId = this.navId; // not really needed..
16237         this.addBullet();
16238     
16239     },
16240     
16241     getActivePanel : function()
16242     {
16243         var r = false;
16244         Roo.each(this.tabs, function(t) {
16245             if (t.active) {
16246                 r = t;
16247                 return false;
16248             }
16249             return null;
16250         });
16251         return r;
16252         
16253     },
16254     getPanelByName : function(n)
16255     {
16256         var r = false;
16257         Roo.each(this.tabs, function(t) {
16258             if (t.tabId == n) {
16259                 r = t;
16260                 return false;
16261             }
16262             return null;
16263         });
16264         return r;
16265     },
16266     indexOfPanel : function(p)
16267     {
16268         var r = false;
16269         Roo.each(this.tabs, function(t,i) {
16270             if (t.tabId == p.tabId) {
16271                 r = i;
16272                 return false;
16273             }
16274             return null;
16275         });
16276         return r;
16277     },
16278     /**
16279      * show a specific panel
16280      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16281      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16282      */
16283     showPanel : function (pan)
16284     {
16285         if(this.transition || typeof(pan) == 'undefined'){
16286             Roo.log("waiting for the transitionend");
16287             return;
16288         }
16289         
16290         if (typeof(pan) == 'number') {
16291             pan = this.tabs[pan];
16292         }
16293         
16294         if (typeof(pan) == 'string') {
16295             pan = this.getPanelByName(pan);
16296         }
16297         
16298         var cur = this.getActivePanel();
16299         
16300         if(!pan || !cur){
16301             Roo.log('pan or acitve pan is undefined');
16302             return false;
16303         }
16304         
16305         if (pan.tabId == this.getActivePanel().tabId) {
16306             return true;
16307         }
16308         
16309         if (false === cur.fireEvent('beforedeactivate')) {
16310             return false;
16311         }
16312         
16313         if(this.bullets > 0 && !Roo.isTouch){
16314             this.setActiveBullet(this.indexOfPanel(pan));
16315         }
16316         
16317         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16318             
16319             this.transition = true;
16320             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16321             var lr = dir == 'next' ? 'left' : 'right';
16322             pan.el.addClass(dir); // or prev
16323             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16324             cur.el.addClass(lr); // or right
16325             pan.el.addClass(lr);
16326             
16327             var _this = this;
16328             cur.el.on('transitionend', function() {
16329                 Roo.log("trans end?");
16330                 
16331                 pan.el.removeClass([lr,dir]);
16332                 pan.setActive(true);
16333                 
16334                 cur.el.removeClass([lr]);
16335                 cur.setActive(false);
16336                 
16337                 _this.transition = false;
16338                 
16339             }, this, { single:  true } );
16340             
16341             return true;
16342         }
16343         
16344         cur.setActive(false);
16345         pan.setActive(true);
16346         
16347         return true;
16348         
16349     },
16350     showPanelNext : function()
16351     {
16352         var i = this.indexOfPanel(this.getActivePanel());
16353         
16354         if (i >= this.tabs.length - 1 && !this.autoslide) {
16355             return;
16356         }
16357         
16358         if (i >= this.tabs.length - 1 && this.autoslide) {
16359             i = -1;
16360         }
16361         
16362         this.showPanel(this.tabs[i+1]);
16363     },
16364     
16365     showPanelPrev : function()
16366     {
16367         var i = this.indexOfPanel(this.getActivePanel());
16368         
16369         if (i  < 1 && !this.autoslide) {
16370             return;
16371         }
16372         
16373         if (i < 1 && this.autoslide) {
16374             i = this.tabs.length;
16375         }
16376         
16377         this.showPanel(this.tabs[i-1]);
16378     },
16379     
16380     
16381     addBullet: function()
16382     {
16383         if(!this.bullets || Roo.isTouch){
16384             return;
16385         }
16386         var ctr = this.el.select('.carousel-bullets',true).first();
16387         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16388         var bullet = ctr.createChild({
16389             cls : 'bullet bullet-' + i
16390         },ctr.dom.lastChild);
16391         
16392         
16393         var _this = this;
16394         
16395         bullet.on('click', (function(e, el, o, ii, t){
16396
16397             e.preventDefault();
16398
16399             this.showPanel(ii);
16400
16401             if(this.autoslide && this.slideFn){
16402                 clearInterval(this.slideFn);
16403                 this.slideFn = window.setInterval(function() {
16404                     _this.showPanelNext();
16405                 }, this.timer);
16406             }
16407
16408         }).createDelegate(this, [i, bullet], true));
16409                 
16410         
16411     },
16412      
16413     setActiveBullet : function(i)
16414     {
16415         if(Roo.isTouch){
16416             return;
16417         }
16418         
16419         Roo.each(this.el.select('.bullet', true).elements, function(el){
16420             el.removeClass('selected');
16421         });
16422
16423         var bullet = this.el.select('.bullet-' + i, true).first();
16424         
16425         if(!bullet){
16426             return;
16427         }
16428         
16429         bullet.addClass('selected');
16430     }
16431     
16432     
16433   
16434 });
16435
16436  
16437
16438  
16439  
16440 Roo.apply(Roo.bootstrap.TabGroup, {
16441     
16442     groups: {},
16443      /**
16444     * register a Navigation Group
16445     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16446     */
16447     register : function(navgrp)
16448     {
16449         this.groups[navgrp.navId] = navgrp;
16450         
16451     },
16452     /**
16453     * fetch a Navigation Group based on the navigation ID
16454     * if one does not exist , it will get created.
16455     * @param {string} the navgroup to add
16456     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16457     */
16458     get: function(navId) {
16459         if (typeof(this.groups[navId]) == 'undefined') {
16460             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16461         }
16462         return this.groups[navId] ;
16463     }
16464     
16465     
16466     
16467 });
16468
16469  /*
16470  * - LGPL
16471  *
16472  * TabPanel
16473  * 
16474  */
16475
16476 /**
16477  * @class Roo.bootstrap.TabPanel
16478  * @extends Roo.bootstrap.Component
16479  * Bootstrap TabPanel class
16480  * @cfg {Boolean} active panel active
16481  * @cfg {String} html panel content
16482  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16483  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16484  * 
16485  * 
16486  * @constructor
16487  * Create a new TabPanel
16488  * @param {Object} config The config object
16489  */
16490
16491 Roo.bootstrap.TabPanel = function(config){
16492     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16493     this.addEvents({
16494         /**
16495              * @event changed
16496              * Fires when the active status changes
16497              * @param {Roo.bootstrap.TabPanel} this
16498              * @param {Boolean} state the new state
16499             
16500          */
16501         'changed': true,
16502         /**
16503              * @event beforedeactivate
16504              * Fires before a tab is de-activated - can be used to do validation on a form.
16505              * @param {Roo.bootstrap.TabPanel} this
16506              * @return {Boolean} false if there is an error
16507             
16508          */
16509         'beforedeactivate': true
16510      });
16511     
16512     this.tabId = this.tabId || Roo.id();
16513   
16514 };
16515
16516 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16517     
16518     active: false,
16519     html: false,
16520     tabId: false,
16521     navId : false,
16522     
16523     getAutoCreate : function(){
16524         var cfg = {
16525             tag: 'div',
16526             // item is needed for carousel - not sure if it has any effect otherwise
16527             cls: 'tab-pane item',
16528             html: this.html || ''
16529         };
16530         
16531         if(this.active){
16532             cfg.cls += ' active';
16533         }
16534         
16535         if(this.tabId){
16536             cfg.tabId = this.tabId;
16537         }
16538         
16539         
16540         return cfg;
16541     },
16542     
16543     initEvents:  function()
16544     {
16545         var p = this.parent();
16546         this.navId = this.navId || p.navId;
16547         
16548         if (typeof(this.navId) != 'undefined') {
16549             // not really needed.. but just in case.. parent should be a NavGroup.
16550             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16551             
16552             tg.register(this);
16553             
16554             var i = tg.tabs.length - 1;
16555             
16556             if(this.active && tg.bullets > 0 && i < tg.bullets){
16557                 tg.setActiveBullet(i);
16558             }
16559         }
16560         
16561     },
16562     
16563     
16564     onRender : function(ct, position)
16565     {
16566        // Roo.log("Call onRender: " + this.xtype);
16567         
16568         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16569         
16570         
16571         
16572         
16573         
16574     },
16575     
16576     setActive: function(state)
16577     {
16578         Roo.log("panel - set active " + this.tabId + "=" + state);
16579         
16580         this.active = state;
16581         if (!state) {
16582             this.el.removeClass('active');
16583             
16584         } else  if (!this.el.hasClass('active')) {
16585             this.el.addClass('active');
16586         }
16587         
16588         this.fireEvent('changed', this, state);
16589     }
16590     
16591     
16592 });
16593  
16594
16595  
16596
16597  /*
16598  * - LGPL
16599  *
16600  * DateField
16601  * 
16602  */
16603
16604 /**
16605  * @class Roo.bootstrap.DateField
16606  * @extends Roo.bootstrap.Input
16607  * Bootstrap DateField class
16608  * @cfg {Number} weekStart default 0
16609  * @cfg {String} viewMode default empty, (months|years)
16610  * @cfg {String} minViewMode default empty, (months|years)
16611  * @cfg {Number} startDate default -Infinity
16612  * @cfg {Number} endDate default Infinity
16613  * @cfg {Boolean} todayHighlight default false
16614  * @cfg {Boolean} todayBtn default false
16615  * @cfg {Boolean} calendarWeeks default false
16616  * @cfg {Object} daysOfWeekDisabled default empty
16617  * @cfg {Boolean} singleMode default false (true | false)
16618  * 
16619  * @cfg {Boolean} keyboardNavigation default true
16620  * @cfg {String} language default en
16621  * 
16622  * @constructor
16623  * Create a new DateField
16624  * @param {Object} config The config object
16625  */
16626
16627 Roo.bootstrap.DateField = function(config){
16628     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16629      this.addEvents({
16630             /**
16631              * @event show
16632              * Fires when this field show.
16633              * @param {Roo.bootstrap.DateField} this
16634              * @param {Mixed} date The date value
16635              */
16636             show : true,
16637             /**
16638              * @event show
16639              * Fires when this field hide.
16640              * @param {Roo.bootstrap.DateField} this
16641              * @param {Mixed} date The date value
16642              */
16643             hide : true,
16644             /**
16645              * @event select
16646              * Fires when select a date.
16647              * @param {Roo.bootstrap.DateField} this
16648              * @param {Mixed} date The date value
16649              */
16650             select : true,
16651             /**
16652              * @event beforeselect
16653              * Fires when before select a date.
16654              * @param {Roo.bootstrap.DateField} this
16655              * @param {Mixed} date The date value
16656              */
16657             beforeselect : true
16658         });
16659 };
16660
16661 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16662     
16663     /**
16664      * @cfg {String} format
16665      * The default date format string which can be overriden for localization support.  The format must be
16666      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16667      */
16668     format : "m/d/y",
16669     /**
16670      * @cfg {String} altFormats
16671      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16672      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16673      */
16674     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16675     
16676     weekStart : 0,
16677     
16678     viewMode : '',
16679     
16680     minViewMode : '',
16681     
16682     todayHighlight : false,
16683     
16684     todayBtn: false,
16685     
16686     language: 'en',
16687     
16688     keyboardNavigation: true,
16689     
16690     calendarWeeks: false,
16691     
16692     startDate: -Infinity,
16693     
16694     endDate: Infinity,
16695     
16696     daysOfWeekDisabled: [],
16697     
16698     _events: [],
16699     
16700     singleMode : false,
16701     
16702     UTCDate: function()
16703     {
16704         return new Date(Date.UTC.apply(Date, arguments));
16705     },
16706     
16707     UTCToday: function()
16708     {
16709         var today = new Date();
16710         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16711     },
16712     
16713     getDate: function() {
16714             var d = this.getUTCDate();
16715             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16716     },
16717     
16718     getUTCDate: function() {
16719             return this.date;
16720     },
16721     
16722     setDate: function(d) {
16723             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16724     },
16725     
16726     setUTCDate: function(d) {
16727             this.date = d;
16728             this.setValue(this.formatDate(this.date));
16729     },
16730         
16731     onRender: function(ct, position)
16732     {
16733         
16734         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16735         
16736         this.language = this.language || 'en';
16737         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16738         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16739         
16740         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16741         this.format = this.format || 'm/d/y';
16742         this.isInline = false;
16743         this.isInput = true;
16744         this.component = this.el.select('.add-on', true).first() || false;
16745         this.component = (this.component && this.component.length === 0) ? false : this.component;
16746         this.hasInput = this.component && this.inputEL().length;
16747         
16748         if (typeof(this.minViewMode === 'string')) {
16749             switch (this.minViewMode) {
16750                 case 'months':
16751                     this.minViewMode = 1;
16752                     break;
16753                 case 'years':
16754                     this.minViewMode = 2;
16755                     break;
16756                 default:
16757                     this.minViewMode = 0;
16758                     break;
16759             }
16760         }
16761         
16762         if (typeof(this.viewMode === 'string')) {
16763             switch (this.viewMode) {
16764                 case 'months':
16765                     this.viewMode = 1;
16766                     break;
16767                 case 'years':
16768                     this.viewMode = 2;
16769                     break;
16770                 default:
16771                     this.viewMode = 0;
16772                     break;
16773             }
16774         }
16775                 
16776         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16777         
16778 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16779         
16780         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16781         
16782         this.picker().on('mousedown', this.onMousedown, this);
16783         this.picker().on('click', this.onClick, this);
16784         
16785         this.picker().addClass('datepicker-dropdown');
16786         
16787         this.startViewMode = this.viewMode;
16788         
16789         if(this.singleMode){
16790             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16791                 v.setVisibilityMode(Roo.Element.DISPLAY);
16792                 v.hide();
16793             });
16794             
16795             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16796                 v.setStyle('width', '189px');
16797             });
16798         }
16799         
16800         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16801             if(!this.calendarWeeks){
16802                 v.remove();
16803                 return;
16804             }
16805             
16806             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16807             v.attr('colspan', function(i, val){
16808                 return parseInt(val) + 1;
16809             });
16810         });
16811                         
16812         
16813         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16814         
16815         this.setStartDate(this.startDate);
16816         this.setEndDate(this.endDate);
16817         
16818         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16819         
16820         this.fillDow();
16821         this.fillMonths();
16822         this.update();
16823         this.showMode();
16824         
16825         if(this.isInline) {
16826             this.show();
16827         }
16828     },
16829     
16830     picker : function()
16831     {
16832         return this.pickerEl;
16833 //        return this.el.select('.datepicker', true).first();
16834     },
16835     
16836     fillDow: function()
16837     {
16838         var dowCnt = this.weekStart;
16839         
16840         var dow = {
16841             tag: 'tr',
16842             cn: [
16843                 
16844             ]
16845         };
16846         
16847         if(this.calendarWeeks){
16848             dow.cn.push({
16849                 tag: 'th',
16850                 cls: 'cw',
16851                 html: '&nbsp;'
16852             })
16853         }
16854         
16855         while (dowCnt < this.weekStart + 7) {
16856             dow.cn.push({
16857                 tag: 'th',
16858                 cls: 'dow',
16859                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16860             });
16861         }
16862         
16863         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16864     },
16865     
16866     fillMonths: function()
16867     {    
16868         var i = 0;
16869         var months = this.picker().select('>.datepicker-months td', true).first();
16870         
16871         months.dom.innerHTML = '';
16872         
16873         while (i < 12) {
16874             var month = {
16875                 tag: 'span',
16876                 cls: 'month',
16877                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16878             };
16879             
16880             months.createChild(month);
16881         }
16882         
16883     },
16884     
16885     update: function()
16886     {
16887         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;
16888         
16889         if (this.date < this.startDate) {
16890             this.viewDate = new Date(this.startDate);
16891         } else if (this.date > this.endDate) {
16892             this.viewDate = new Date(this.endDate);
16893         } else {
16894             this.viewDate = new Date(this.date);
16895         }
16896         
16897         this.fill();
16898     },
16899     
16900     fill: function() 
16901     {
16902         var d = new Date(this.viewDate),
16903                 year = d.getUTCFullYear(),
16904                 month = d.getUTCMonth(),
16905                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16906                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16907                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16908                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16909                 currentDate = this.date && this.date.valueOf(),
16910                 today = this.UTCToday();
16911         
16912         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16913         
16914 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16915         
16916 //        this.picker.select('>tfoot th.today').
16917 //                                              .text(dates[this.language].today)
16918 //                                              .toggle(this.todayBtn !== false);
16919     
16920         this.updateNavArrows();
16921         this.fillMonths();
16922                                                 
16923         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16924         
16925         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16926          
16927         prevMonth.setUTCDate(day);
16928         
16929         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16930         
16931         var nextMonth = new Date(prevMonth);
16932         
16933         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16934         
16935         nextMonth = nextMonth.valueOf();
16936         
16937         var fillMonths = false;
16938         
16939         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16940         
16941         while(prevMonth.valueOf() < nextMonth) {
16942             var clsName = '';
16943             
16944             if (prevMonth.getUTCDay() === this.weekStart) {
16945                 if(fillMonths){
16946                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16947                 }
16948                     
16949                 fillMonths = {
16950                     tag: 'tr',
16951                     cn: []
16952                 };
16953                 
16954                 if(this.calendarWeeks){
16955                     // ISO 8601: First week contains first thursday.
16956                     // ISO also states week starts on Monday, but we can be more abstract here.
16957                     var
16958                     // Start of current week: based on weekstart/current date
16959                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16960                     // Thursday of this week
16961                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16962                     // First Thursday of year, year from thursday
16963                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16964                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16965                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16966                     
16967                     fillMonths.cn.push({
16968                         tag: 'td',
16969                         cls: 'cw',
16970                         html: calWeek
16971                     });
16972                 }
16973             }
16974             
16975             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16976                 clsName += ' old';
16977             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16978                 clsName += ' new';
16979             }
16980             if (this.todayHighlight &&
16981                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16982                 prevMonth.getUTCMonth() == today.getMonth() &&
16983                 prevMonth.getUTCDate() == today.getDate()) {
16984                 clsName += ' today';
16985             }
16986             
16987             if (currentDate && prevMonth.valueOf() === currentDate) {
16988                 clsName += ' active';
16989             }
16990             
16991             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16992                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16993                     clsName += ' disabled';
16994             }
16995             
16996             fillMonths.cn.push({
16997                 tag: 'td',
16998                 cls: 'day ' + clsName,
16999                 html: prevMonth.getDate()
17000             });
17001             
17002             prevMonth.setDate(prevMonth.getDate()+1);
17003         }
17004           
17005         var currentYear = this.date && this.date.getUTCFullYear();
17006         var currentMonth = this.date && this.date.getUTCMonth();
17007         
17008         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17009         
17010         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17011             v.removeClass('active');
17012             
17013             if(currentYear === year && k === currentMonth){
17014                 v.addClass('active');
17015             }
17016             
17017             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17018                 v.addClass('disabled');
17019             }
17020             
17021         });
17022         
17023         
17024         year = parseInt(year/10, 10) * 10;
17025         
17026         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17027         
17028         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17029         
17030         year -= 1;
17031         for (var i = -1; i < 11; i++) {
17032             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17033                 tag: 'span',
17034                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17035                 html: year
17036             });
17037             
17038             year += 1;
17039         }
17040     },
17041     
17042     showMode: function(dir) 
17043     {
17044         if (dir) {
17045             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17046         }
17047         
17048         Roo.each(this.picker().select('>div',true).elements, function(v){
17049             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17050             v.hide();
17051         });
17052         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17053     },
17054     
17055     place: function()
17056     {
17057         if(this.isInline) {
17058             return;
17059         }
17060         
17061         this.picker().removeClass(['bottom', 'top']);
17062         
17063         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17064             /*
17065              * place to the top of element!
17066              *
17067              */
17068             
17069             this.picker().addClass('top');
17070             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17071             
17072             return;
17073         }
17074         
17075         this.picker().addClass('bottom');
17076         
17077         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17078     },
17079     
17080     parseDate : function(value)
17081     {
17082         if(!value || value instanceof Date){
17083             return value;
17084         }
17085         var v = Date.parseDate(value, this.format);
17086         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17087             v = Date.parseDate(value, 'Y-m-d');
17088         }
17089         if(!v && this.altFormats){
17090             if(!this.altFormatsArray){
17091                 this.altFormatsArray = this.altFormats.split("|");
17092             }
17093             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17094                 v = Date.parseDate(value, this.altFormatsArray[i]);
17095             }
17096         }
17097         return v;
17098     },
17099     
17100     formatDate : function(date, fmt)
17101     {   
17102         return (!date || !(date instanceof Date)) ?
17103         date : date.dateFormat(fmt || this.format);
17104     },
17105     
17106     onFocus : function()
17107     {
17108         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17109         this.show();
17110     },
17111     
17112     onBlur : function()
17113     {
17114         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17115         
17116         var d = this.inputEl().getValue();
17117         
17118         this.setValue(d);
17119                 
17120         this.hide();
17121     },
17122     
17123     show : function()
17124     {
17125         this.picker().show();
17126         this.update();
17127         this.place();
17128         
17129         this.fireEvent('show', this, this.date);
17130     },
17131     
17132     hide : function()
17133     {
17134         if(this.isInline) {
17135             return;
17136         }
17137         this.picker().hide();
17138         this.viewMode = this.startViewMode;
17139         this.showMode();
17140         
17141         this.fireEvent('hide', this, this.date);
17142         
17143     },
17144     
17145     onMousedown: function(e)
17146     {
17147         e.stopPropagation();
17148         e.preventDefault();
17149     },
17150     
17151     keyup: function(e)
17152     {
17153         Roo.bootstrap.DateField.superclass.keyup.call(this);
17154         this.update();
17155     },
17156
17157     setValue: function(v)
17158     {
17159         if(this.fireEvent('beforeselect', this, v) !== false){
17160             var d = new Date(this.parseDate(v) ).clearTime();
17161         
17162             if(isNaN(d.getTime())){
17163                 this.date = this.viewDate = '';
17164                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17165                 return;
17166             }
17167
17168             v = this.formatDate(d);
17169
17170             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17171
17172             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17173
17174             this.update();
17175
17176             this.fireEvent('select', this, this.date);
17177         }
17178     },
17179     
17180     getValue: function()
17181     {
17182         return this.formatDate(this.date);
17183     },
17184     
17185     fireKey: function(e)
17186     {
17187         if (!this.picker().isVisible()){
17188             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17189                 this.show();
17190             }
17191             return;
17192         }
17193         
17194         var dateChanged = false,
17195         dir, day, month,
17196         newDate, newViewDate;
17197         
17198         switch(e.keyCode){
17199             case 27: // escape
17200                 this.hide();
17201                 e.preventDefault();
17202                 break;
17203             case 37: // left
17204             case 39: // right
17205                 if (!this.keyboardNavigation) {
17206                     break;
17207                 }
17208                 dir = e.keyCode == 37 ? -1 : 1;
17209                 
17210                 if (e.ctrlKey){
17211                     newDate = this.moveYear(this.date, dir);
17212                     newViewDate = this.moveYear(this.viewDate, dir);
17213                 } else if (e.shiftKey){
17214                     newDate = this.moveMonth(this.date, dir);
17215                     newViewDate = this.moveMonth(this.viewDate, dir);
17216                 } else {
17217                     newDate = new Date(this.date);
17218                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17219                     newViewDate = new Date(this.viewDate);
17220                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17221                 }
17222                 if (this.dateWithinRange(newDate)){
17223                     this.date = newDate;
17224                     this.viewDate = newViewDate;
17225                     this.setValue(this.formatDate(this.date));
17226 //                    this.update();
17227                     e.preventDefault();
17228                     dateChanged = true;
17229                 }
17230                 break;
17231             case 38: // up
17232             case 40: // down
17233                 if (!this.keyboardNavigation) {
17234                     break;
17235                 }
17236                 dir = e.keyCode == 38 ? -1 : 1;
17237                 if (e.ctrlKey){
17238                     newDate = this.moveYear(this.date, dir);
17239                     newViewDate = this.moveYear(this.viewDate, dir);
17240                 } else if (e.shiftKey){
17241                     newDate = this.moveMonth(this.date, dir);
17242                     newViewDate = this.moveMonth(this.viewDate, dir);
17243                 } else {
17244                     newDate = new Date(this.date);
17245                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17246                     newViewDate = new Date(this.viewDate);
17247                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17248                 }
17249                 if (this.dateWithinRange(newDate)){
17250                     this.date = newDate;
17251                     this.viewDate = newViewDate;
17252                     this.setValue(this.formatDate(this.date));
17253 //                    this.update();
17254                     e.preventDefault();
17255                     dateChanged = true;
17256                 }
17257                 break;
17258             case 13: // enter
17259                 this.setValue(this.formatDate(this.date));
17260                 this.hide();
17261                 e.preventDefault();
17262                 break;
17263             case 9: // tab
17264                 this.setValue(this.formatDate(this.date));
17265                 this.hide();
17266                 break;
17267             case 16: // shift
17268             case 17: // ctrl
17269             case 18: // alt
17270                 break;
17271             default :
17272                 this.hide();
17273                 
17274         }
17275     },
17276     
17277     
17278     onClick: function(e) 
17279     {
17280         e.stopPropagation();
17281         e.preventDefault();
17282         
17283         var target = e.getTarget();
17284         
17285         if(target.nodeName.toLowerCase() === 'i'){
17286             target = Roo.get(target).dom.parentNode;
17287         }
17288         
17289         var nodeName = target.nodeName;
17290         var className = target.className;
17291         var html = target.innerHTML;
17292         //Roo.log(nodeName);
17293         
17294         switch(nodeName.toLowerCase()) {
17295             case 'th':
17296                 switch(className) {
17297                     case 'switch':
17298                         this.showMode(1);
17299                         break;
17300                     case 'prev':
17301                     case 'next':
17302                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17303                         switch(this.viewMode){
17304                                 case 0:
17305                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17306                                         break;
17307                                 case 1:
17308                                 case 2:
17309                                         this.viewDate = this.moveYear(this.viewDate, dir);
17310                                         break;
17311                         }
17312                         this.fill();
17313                         break;
17314                     case 'today':
17315                         var date = new Date();
17316                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17317 //                        this.fill()
17318                         this.setValue(this.formatDate(this.date));
17319                         
17320                         this.hide();
17321                         break;
17322                 }
17323                 break;
17324             case 'span':
17325                 if (className.indexOf('disabled') < 0) {
17326                     this.viewDate.setUTCDate(1);
17327                     if (className.indexOf('month') > -1) {
17328                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17329                     } else {
17330                         var year = parseInt(html, 10) || 0;
17331                         this.viewDate.setUTCFullYear(year);
17332                         
17333                     }
17334                     
17335                     if(this.singleMode){
17336                         this.setValue(this.formatDate(this.viewDate));
17337                         this.hide();
17338                         return;
17339                     }
17340                     
17341                     this.showMode(-1);
17342                     this.fill();
17343                 }
17344                 break;
17345                 
17346             case 'td':
17347                 //Roo.log(className);
17348                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17349                     var day = parseInt(html, 10) || 1;
17350                     var year = this.viewDate.getUTCFullYear(),
17351                         month = this.viewDate.getUTCMonth();
17352
17353                     if (className.indexOf('old') > -1) {
17354                         if(month === 0 ){
17355                             month = 11;
17356                             year -= 1;
17357                         }else{
17358                             month -= 1;
17359                         }
17360                     } else if (className.indexOf('new') > -1) {
17361                         if (month == 11) {
17362                             month = 0;
17363                             year += 1;
17364                         } else {
17365                             month += 1;
17366                         }
17367                     }
17368                     //Roo.log([year,month,day]);
17369                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17370                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17371 //                    this.fill();
17372                     //Roo.log(this.formatDate(this.date));
17373                     this.setValue(this.formatDate(this.date));
17374                     this.hide();
17375                 }
17376                 break;
17377         }
17378     },
17379     
17380     setStartDate: function(startDate)
17381     {
17382         this.startDate = startDate || -Infinity;
17383         if (this.startDate !== -Infinity) {
17384             this.startDate = this.parseDate(this.startDate);
17385         }
17386         this.update();
17387         this.updateNavArrows();
17388     },
17389
17390     setEndDate: function(endDate)
17391     {
17392         this.endDate = endDate || Infinity;
17393         if (this.endDate !== Infinity) {
17394             this.endDate = this.parseDate(this.endDate);
17395         }
17396         this.update();
17397         this.updateNavArrows();
17398     },
17399     
17400     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17401     {
17402         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17403         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17404             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17405         }
17406         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17407             return parseInt(d, 10);
17408         });
17409         this.update();
17410         this.updateNavArrows();
17411     },
17412     
17413     updateNavArrows: function() 
17414     {
17415         if(this.singleMode){
17416             return;
17417         }
17418         
17419         var d = new Date(this.viewDate),
17420         year = d.getUTCFullYear(),
17421         month = d.getUTCMonth();
17422         
17423         Roo.each(this.picker().select('.prev', true).elements, function(v){
17424             v.show();
17425             switch (this.viewMode) {
17426                 case 0:
17427
17428                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17429                         v.hide();
17430                     }
17431                     break;
17432                 case 1:
17433                 case 2:
17434                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17435                         v.hide();
17436                     }
17437                     break;
17438             }
17439         });
17440         
17441         Roo.each(this.picker().select('.next', true).elements, function(v){
17442             v.show();
17443             switch (this.viewMode) {
17444                 case 0:
17445
17446                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17447                         v.hide();
17448                     }
17449                     break;
17450                 case 1:
17451                 case 2:
17452                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17453                         v.hide();
17454                     }
17455                     break;
17456             }
17457         })
17458     },
17459     
17460     moveMonth: function(date, dir)
17461     {
17462         if (!dir) {
17463             return date;
17464         }
17465         var new_date = new Date(date.valueOf()),
17466         day = new_date.getUTCDate(),
17467         month = new_date.getUTCMonth(),
17468         mag = Math.abs(dir),
17469         new_month, test;
17470         dir = dir > 0 ? 1 : -1;
17471         if (mag == 1){
17472             test = dir == -1
17473             // If going back one month, make sure month is not current month
17474             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17475             ? function(){
17476                 return new_date.getUTCMonth() == month;
17477             }
17478             // If going forward one month, make sure month is as expected
17479             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17480             : function(){
17481                 return new_date.getUTCMonth() != new_month;
17482             };
17483             new_month = month + dir;
17484             new_date.setUTCMonth(new_month);
17485             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17486             if (new_month < 0 || new_month > 11) {
17487                 new_month = (new_month + 12) % 12;
17488             }
17489         } else {
17490             // For magnitudes >1, move one month at a time...
17491             for (var i=0; i<mag; i++) {
17492                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17493                 new_date = this.moveMonth(new_date, dir);
17494             }
17495             // ...then reset the day, keeping it in the new month
17496             new_month = new_date.getUTCMonth();
17497             new_date.setUTCDate(day);
17498             test = function(){
17499                 return new_month != new_date.getUTCMonth();
17500             };
17501         }
17502         // Common date-resetting loop -- if date is beyond end of month, make it
17503         // end of month
17504         while (test()){
17505             new_date.setUTCDate(--day);
17506             new_date.setUTCMonth(new_month);
17507         }
17508         return new_date;
17509     },
17510
17511     moveYear: function(date, dir)
17512     {
17513         return this.moveMonth(date, dir*12);
17514     },
17515
17516     dateWithinRange: function(date)
17517     {
17518         return date >= this.startDate && date <= this.endDate;
17519     },
17520
17521     
17522     remove: function() 
17523     {
17524         this.picker().remove();
17525     }
17526    
17527 });
17528
17529 Roo.apply(Roo.bootstrap.DateField,  {
17530     
17531     head : {
17532         tag: 'thead',
17533         cn: [
17534         {
17535             tag: 'tr',
17536             cn: [
17537             {
17538                 tag: 'th',
17539                 cls: 'prev',
17540                 html: '<i class="fa fa-arrow-left"/>'
17541             },
17542             {
17543                 tag: 'th',
17544                 cls: 'switch',
17545                 colspan: '5'
17546             },
17547             {
17548                 tag: 'th',
17549                 cls: 'next',
17550                 html: '<i class="fa fa-arrow-right"/>'
17551             }
17552
17553             ]
17554         }
17555         ]
17556     },
17557     
17558     content : {
17559         tag: 'tbody',
17560         cn: [
17561         {
17562             tag: 'tr',
17563             cn: [
17564             {
17565                 tag: 'td',
17566                 colspan: '7'
17567             }
17568             ]
17569         }
17570         ]
17571     },
17572     
17573     footer : {
17574         tag: 'tfoot',
17575         cn: [
17576         {
17577             tag: 'tr',
17578             cn: [
17579             {
17580                 tag: 'th',
17581                 colspan: '7',
17582                 cls: 'today'
17583             }
17584                     
17585             ]
17586         }
17587         ]
17588     },
17589     
17590     dates:{
17591         en: {
17592             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17593             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17594             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17595             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17596             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17597             today: "Today"
17598         }
17599     },
17600     
17601     modes: [
17602     {
17603         clsName: 'days',
17604         navFnc: 'Month',
17605         navStep: 1
17606     },
17607     {
17608         clsName: 'months',
17609         navFnc: 'FullYear',
17610         navStep: 1
17611     },
17612     {
17613         clsName: 'years',
17614         navFnc: 'FullYear',
17615         navStep: 10
17616     }]
17617 });
17618
17619 Roo.apply(Roo.bootstrap.DateField,  {
17620   
17621     template : {
17622         tag: 'div',
17623         cls: 'datepicker dropdown-menu roo-dynamic',
17624         cn: [
17625         {
17626             tag: 'div',
17627             cls: 'datepicker-days',
17628             cn: [
17629             {
17630                 tag: 'table',
17631                 cls: 'table-condensed',
17632                 cn:[
17633                 Roo.bootstrap.DateField.head,
17634                 {
17635                     tag: 'tbody'
17636                 },
17637                 Roo.bootstrap.DateField.footer
17638                 ]
17639             }
17640             ]
17641         },
17642         {
17643             tag: 'div',
17644             cls: 'datepicker-months',
17645             cn: [
17646             {
17647                 tag: 'table',
17648                 cls: 'table-condensed',
17649                 cn:[
17650                 Roo.bootstrap.DateField.head,
17651                 Roo.bootstrap.DateField.content,
17652                 Roo.bootstrap.DateField.footer
17653                 ]
17654             }
17655             ]
17656         },
17657         {
17658             tag: 'div',
17659             cls: 'datepicker-years',
17660             cn: [
17661             {
17662                 tag: 'table',
17663                 cls: 'table-condensed',
17664                 cn:[
17665                 Roo.bootstrap.DateField.head,
17666                 Roo.bootstrap.DateField.content,
17667                 Roo.bootstrap.DateField.footer
17668                 ]
17669             }
17670             ]
17671         }
17672         ]
17673     }
17674 });
17675
17676  
17677
17678  /*
17679  * - LGPL
17680  *
17681  * TimeField
17682  * 
17683  */
17684
17685 /**
17686  * @class Roo.bootstrap.TimeField
17687  * @extends Roo.bootstrap.Input
17688  * Bootstrap DateField class
17689  * 
17690  * 
17691  * @constructor
17692  * Create a new TimeField
17693  * @param {Object} config The config object
17694  */
17695
17696 Roo.bootstrap.TimeField = function(config){
17697     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17698     this.addEvents({
17699             /**
17700              * @event show
17701              * Fires when this field show.
17702              * @param {Roo.bootstrap.DateField} thisthis
17703              * @param {Mixed} date The date value
17704              */
17705             show : true,
17706             /**
17707              * @event show
17708              * Fires when this field hide.
17709              * @param {Roo.bootstrap.DateField} this
17710              * @param {Mixed} date The date value
17711              */
17712             hide : true,
17713             /**
17714              * @event select
17715              * Fires when select a date.
17716              * @param {Roo.bootstrap.DateField} this
17717              * @param {Mixed} date The date value
17718              */
17719             select : true
17720         });
17721 };
17722
17723 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17724     
17725     /**
17726      * @cfg {String} format
17727      * The default time format string which can be overriden for localization support.  The format must be
17728      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17729      */
17730     format : "H:i",
17731        
17732     onRender: function(ct, position)
17733     {
17734         
17735         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17736                 
17737         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17738         
17739         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17740         
17741         this.pop = this.picker().select('>.datepicker-time',true).first();
17742         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17743         
17744         this.picker().on('mousedown', this.onMousedown, this);
17745         this.picker().on('click', this.onClick, this);
17746         
17747         this.picker().addClass('datepicker-dropdown');
17748     
17749         this.fillTime();
17750         this.update();
17751             
17752         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17753         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17754         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17755         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17756         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17757         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17758
17759     },
17760     
17761     fireKey: function(e){
17762         if (!this.picker().isVisible()){
17763             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17764                 this.show();
17765             }
17766             return;
17767         }
17768
17769         e.preventDefault();
17770         
17771         switch(e.keyCode){
17772             case 27: // escape
17773                 this.hide();
17774                 break;
17775             case 37: // left
17776             case 39: // right
17777                 this.onTogglePeriod();
17778                 break;
17779             case 38: // up
17780                 this.onIncrementMinutes();
17781                 break;
17782             case 40: // down
17783                 this.onDecrementMinutes();
17784                 break;
17785             case 13: // enter
17786             case 9: // tab
17787                 this.setTime();
17788                 break;
17789         }
17790     },
17791     
17792     onClick: function(e) {
17793         e.stopPropagation();
17794         e.preventDefault();
17795     },
17796     
17797     picker : function()
17798     {
17799         return this.el.select('.datepicker', true).first();
17800     },
17801     
17802     fillTime: function()
17803     {    
17804         var time = this.pop.select('tbody', true).first();
17805         
17806         time.dom.innerHTML = '';
17807         
17808         time.createChild({
17809             tag: 'tr',
17810             cn: [
17811                 {
17812                     tag: 'td',
17813                     cn: [
17814                         {
17815                             tag: 'a',
17816                             href: '#',
17817                             cls: 'btn',
17818                             cn: [
17819                                 {
17820                                     tag: 'span',
17821                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17822                                 }
17823                             ]
17824                         } 
17825                     ]
17826                 },
17827                 {
17828                     tag: 'td',
17829                     cls: 'separator'
17830                 },
17831                 {
17832                     tag: 'td',
17833                     cn: [
17834                         {
17835                             tag: 'a',
17836                             href: '#',
17837                             cls: 'btn',
17838                             cn: [
17839                                 {
17840                                     tag: 'span',
17841                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17842                                 }
17843                             ]
17844                         }
17845                     ]
17846                 },
17847                 {
17848                     tag: 'td',
17849                     cls: 'separator'
17850                 }
17851             ]
17852         });
17853         
17854         time.createChild({
17855             tag: 'tr',
17856             cn: [
17857                 {
17858                     tag: 'td',
17859                     cn: [
17860                         {
17861                             tag: 'span',
17862                             cls: 'timepicker-hour',
17863                             html: '00'
17864                         }  
17865                     ]
17866                 },
17867                 {
17868                     tag: 'td',
17869                     cls: 'separator',
17870                     html: ':'
17871                 },
17872                 {
17873                     tag: 'td',
17874                     cn: [
17875                         {
17876                             tag: 'span',
17877                             cls: 'timepicker-minute',
17878                             html: '00'
17879                         }  
17880                     ]
17881                 },
17882                 {
17883                     tag: 'td',
17884                     cls: 'separator'
17885                 },
17886                 {
17887                     tag: 'td',
17888                     cn: [
17889                         {
17890                             tag: 'button',
17891                             type: 'button',
17892                             cls: 'btn btn-primary period',
17893                             html: 'AM'
17894                             
17895                         }
17896                     ]
17897                 }
17898             ]
17899         });
17900         
17901         time.createChild({
17902             tag: 'tr',
17903             cn: [
17904                 {
17905                     tag: 'td',
17906                     cn: [
17907                         {
17908                             tag: 'a',
17909                             href: '#',
17910                             cls: 'btn',
17911                             cn: [
17912                                 {
17913                                     tag: 'span',
17914                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17915                                 }
17916                             ]
17917                         }
17918                     ]
17919                 },
17920                 {
17921                     tag: 'td',
17922                     cls: 'separator'
17923                 },
17924                 {
17925                     tag: 'td',
17926                     cn: [
17927                         {
17928                             tag: 'a',
17929                             href: '#',
17930                             cls: 'btn',
17931                             cn: [
17932                                 {
17933                                     tag: 'span',
17934                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17935                                 }
17936                             ]
17937                         }
17938                     ]
17939                 },
17940                 {
17941                     tag: 'td',
17942                     cls: 'separator'
17943                 }
17944             ]
17945         });
17946         
17947     },
17948     
17949     update: function()
17950     {
17951         
17952         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17953         
17954         this.fill();
17955     },
17956     
17957     fill: function() 
17958     {
17959         var hours = this.time.getHours();
17960         var minutes = this.time.getMinutes();
17961         var period = 'AM';
17962         
17963         if(hours > 11){
17964             period = 'PM';
17965         }
17966         
17967         if(hours == 0){
17968             hours = 12;
17969         }
17970         
17971         
17972         if(hours > 12){
17973             hours = hours - 12;
17974         }
17975         
17976         if(hours < 10){
17977             hours = '0' + hours;
17978         }
17979         
17980         if(minutes < 10){
17981             minutes = '0' + minutes;
17982         }
17983         
17984         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17985         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17986         this.pop.select('button', true).first().dom.innerHTML = period;
17987         
17988     },
17989     
17990     place: function()
17991     {   
17992         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17993         
17994         var cls = ['bottom'];
17995         
17996         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17997             cls.pop();
17998             cls.push('top');
17999         }
18000         
18001         cls.push('right');
18002         
18003         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18004             cls.pop();
18005             cls.push('left');
18006         }
18007         
18008         this.picker().addClass(cls.join('-'));
18009         
18010         var _this = this;
18011         
18012         Roo.each(cls, function(c){
18013             if(c == 'bottom'){
18014                 _this.picker().setTop(_this.inputEl().getHeight());
18015                 return;
18016             }
18017             if(c == 'top'){
18018                 _this.picker().setTop(0 - _this.picker().getHeight());
18019                 return;
18020             }
18021             
18022             if(c == 'left'){
18023                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18024                 return;
18025             }
18026             if(c == 'right'){
18027                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18028                 return;
18029             }
18030         });
18031         
18032     },
18033   
18034     onFocus : function()
18035     {
18036         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18037         this.show();
18038     },
18039     
18040     onBlur : function()
18041     {
18042         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18043         this.hide();
18044     },
18045     
18046     show : function()
18047     {
18048         this.picker().show();
18049         this.pop.show();
18050         this.update();
18051         this.place();
18052         
18053         this.fireEvent('show', this, this.date);
18054     },
18055     
18056     hide : function()
18057     {
18058         this.picker().hide();
18059         this.pop.hide();
18060         
18061         this.fireEvent('hide', this, this.date);
18062     },
18063     
18064     setTime : function()
18065     {
18066         this.hide();
18067         this.setValue(this.time.format(this.format));
18068         
18069         this.fireEvent('select', this, this.date);
18070         
18071         
18072     },
18073     
18074     onMousedown: function(e){
18075         e.stopPropagation();
18076         e.preventDefault();
18077     },
18078     
18079     onIncrementHours: function()
18080     {
18081         Roo.log('onIncrementHours');
18082         this.time = this.time.add(Date.HOUR, 1);
18083         this.update();
18084         
18085     },
18086     
18087     onDecrementHours: function()
18088     {
18089         Roo.log('onDecrementHours');
18090         this.time = this.time.add(Date.HOUR, -1);
18091         this.update();
18092     },
18093     
18094     onIncrementMinutes: function()
18095     {
18096         Roo.log('onIncrementMinutes');
18097         this.time = this.time.add(Date.MINUTE, 1);
18098         this.update();
18099     },
18100     
18101     onDecrementMinutes: function()
18102     {
18103         Roo.log('onDecrementMinutes');
18104         this.time = this.time.add(Date.MINUTE, -1);
18105         this.update();
18106     },
18107     
18108     onTogglePeriod: function()
18109     {
18110         Roo.log('onTogglePeriod');
18111         this.time = this.time.add(Date.HOUR, 12);
18112         this.update();
18113     }
18114     
18115    
18116 });
18117
18118 Roo.apply(Roo.bootstrap.TimeField,  {
18119     
18120     content : {
18121         tag: 'tbody',
18122         cn: [
18123             {
18124                 tag: 'tr',
18125                 cn: [
18126                 {
18127                     tag: 'td',
18128                     colspan: '7'
18129                 }
18130                 ]
18131             }
18132         ]
18133     },
18134     
18135     footer : {
18136         tag: 'tfoot',
18137         cn: [
18138             {
18139                 tag: 'tr',
18140                 cn: [
18141                 {
18142                     tag: 'th',
18143                     colspan: '7',
18144                     cls: '',
18145                     cn: [
18146                         {
18147                             tag: 'button',
18148                             cls: 'btn btn-info ok',
18149                             html: 'OK'
18150                         }
18151                     ]
18152                 }
18153
18154                 ]
18155             }
18156         ]
18157     }
18158 });
18159
18160 Roo.apply(Roo.bootstrap.TimeField,  {
18161   
18162     template : {
18163         tag: 'div',
18164         cls: 'datepicker dropdown-menu',
18165         cn: [
18166             {
18167                 tag: 'div',
18168                 cls: 'datepicker-time',
18169                 cn: [
18170                 {
18171                     tag: 'table',
18172                     cls: 'table-condensed',
18173                     cn:[
18174                     Roo.bootstrap.TimeField.content,
18175                     Roo.bootstrap.TimeField.footer
18176                     ]
18177                 }
18178                 ]
18179             }
18180         ]
18181     }
18182 });
18183
18184  
18185
18186  /*
18187  * - LGPL
18188  *
18189  * MonthField
18190  * 
18191  */
18192
18193 /**
18194  * @class Roo.bootstrap.MonthField
18195  * @extends Roo.bootstrap.Input
18196  * Bootstrap MonthField class
18197  * 
18198  * @cfg {String} language default en
18199  * 
18200  * @constructor
18201  * Create a new MonthField
18202  * @param {Object} config The config object
18203  */
18204
18205 Roo.bootstrap.MonthField = function(config){
18206     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18207     
18208     this.addEvents({
18209         /**
18210          * @event show
18211          * Fires when this field show.
18212          * @param {Roo.bootstrap.MonthField} this
18213          * @param {Mixed} date The date value
18214          */
18215         show : true,
18216         /**
18217          * @event show
18218          * Fires when this field hide.
18219          * @param {Roo.bootstrap.MonthField} this
18220          * @param {Mixed} date The date value
18221          */
18222         hide : true,
18223         /**
18224          * @event select
18225          * Fires when select a date.
18226          * @param {Roo.bootstrap.MonthField} this
18227          * @param {String} oldvalue The old value
18228          * @param {String} newvalue The new value
18229          */
18230         select : true
18231     });
18232 };
18233
18234 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18235     
18236     onRender: function(ct, position)
18237     {
18238         
18239         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18240         
18241         this.language = this.language || 'en';
18242         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18243         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18244         
18245         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18246         this.isInline = false;
18247         this.isInput = true;
18248         this.component = this.el.select('.add-on', true).first() || false;
18249         this.component = (this.component && this.component.length === 0) ? false : this.component;
18250         this.hasInput = this.component && this.inputEL().length;
18251         
18252         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18253         
18254         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18255         
18256         this.picker().on('mousedown', this.onMousedown, this);
18257         this.picker().on('click', this.onClick, this);
18258         
18259         this.picker().addClass('datepicker-dropdown');
18260         
18261         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18262             v.setStyle('width', '189px');
18263         });
18264         
18265         this.fillMonths();
18266         
18267         this.update();
18268         
18269         if(this.isInline) {
18270             this.show();
18271         }
18272         
18273     },
18274     
18275     setValue: function(v, suppressEvent)
18276     {   
18277         var o = this.getValue();
18278         
18279         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18280         
18281         this.update();
18282
18283         if(suppressEvent !== true){
18284             this.fireEvent('select', this, o, v);
18285         }
18286         
18287     },
18288     
18289     getValue: function()
18290     {
18291         return this.value;
18292     },
18293     
18294     onClick: function(e) 
18295     {
18296         e.stopPropagation();
18297         e.preventDefault();
18298         
18299         var target = e.getTarget();
18300         
18301         if(target.nodeName.toLowerCase() === 'i'){
18302             target = Roo.get(target).dom.parentNode;
18303         }
18304         
18305         var nodeName = target.nodeName;
18306         var className = target.className;
18307         var html = target.innerHTML;
18308         
18309         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18310             return;
18311         }
18312         
18313         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18314         
18315         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18316         
18317         this.hide();
18318                         
18319     },
18320     
18321     picker : function()
18322     {
18323         return this.pickerEl;
18324     },
18325     
18326     fillMonths: function()
18327     {    
18328         var i = 0;
18329         var months = this.picker().select('>.datepicker-months td', true).first();
18330         
18331         months.dom.innerHTML = '';
18332         
18333         while (i < 12) {
18334             var month = {
18335                 tag: 'span',
18336                 cls: 'month',
18337                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18338             };
18339             
18340             months.createChild(month);
18341         }
18342         
18343     },
18344     
18345     update: function()
18346     {
18347         var _this = this;
18348         
18349         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18350             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18351         }
18352         
18353         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18354             e.removeClass('active');
18355             
18356             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18357                 e.addClass('active');
18358             }
18359         })
18360     },
18361     
18362     place: function()
18363     {
18364         if(this.isInline) {
18365             return;
18366         }
18367         
18368         this.picker().removeClass(['bottom', 'top']);
18369         
18370         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18371             /*
18372              * place to the top of element!
18373              *
18374              */
18375             
18376             this.picker().addClass('top');
18377             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18378             
18379             return;
18380         }
18381         
18382         this.picker().addClass('bottom');
18383         
18384         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18385     },
18386     
18387     onFocus : function()
18388     {
18389         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18390         this.show();
18391     },
18392     
18393     onBlur : function()
18394     {
18395         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18396         
18397         var d = this.inputEl().getValue();
18398         
18399         this.setValue(d);
18400                 
18401         this.hide();
18402     },
18403     
18404     show : function()
18405     {
18406         this.picker().show();
18407         this.picker().select('>.datepicker-months', true).first().show();
18408         this.update();
18409         this.place();
18410         
18411         this.fireEvent('show', this, this.date);
18412     },
18413     
18414     hide : function()
18415     {
18416         if(this.isInline) {
18417             return;
18418         }
18419         this.picker().hide();
18420         this.fireEvent('hide', this, this.date);
18421         
18422     },
18423     
18424     onMousedown: function(e)
18425     {
18426         e.stopPropagation();
18427         e.preventDefault();
18428     },
18429     
18430     keyup: function(e)
18431     {
18432         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18433         this.update();
18434     },
18435
18436     fireKey: function(e)
18437     {
18438         if (!this.picker().isVisible()){
18439             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18440                 this.show();
18441             }
18442             return;
18443         }
18444         
18445         var dir;
18446         
18447         switch(e.keyCode){
18448             case 27: // escape
18449                 this.hide();
18450                 e.preventDefault();
18451                 break;
18452             case 37: // left
18453             case 39: // right
18454                 dir = e.keyCode == 37 ? -1 : 1;
18455                 
18456                 this.vIndex = this.vIndex + dir;
18457                 
18458                 if(this.vIndex < 0){
18459                     this.vIndex = 0;
18460                 }
18461                 
18462                 if(this.vIndex > 11){
18463                     this.vIndex = 11;
18464                 }
18465                 
18466                 if(isNaN(this.vIndex)){
18467                     this.vIndex = 0;
18468                 }
18469                 
18470                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18471                 
18472                 break;
18473             case 38: // up
18474             case 40: // down
18475                 
18476                 dir = e.keyCode == 38 ? -1 : 1;
18477                 
18478                 this.vIndex = this.vIndex + dir * 4;
18479                 
18480                 if(this.vIndex < 0){
18481                     this.vIndex = 0;
18482                 }
18483                 
18484                 if(this.vIndex > 11){
18485                     this.vIndex = 11;
18486                 }
18487                 
18488                 if(isNaN(this.vIndex)){
18489                     this.vIndex = 0;
18490                 }
18491                 
18492                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18493                 break;
18494                 
18495             case 13: // enter
18496                 
18497                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18498                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18499                 }
18500                 
18501                 this.hide();
18502                 e.preventDefault();
18503                 break;
18504             case 9: // tab
18505                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18506                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18507                 }
18508                 this.hide();
18509                 break;
18510             case 16: // shift
18511             case 17: // ctrl
18512             case 18: // alt
18513                 break;
18514             default :
18515                 this.hide();
18516                 
18517         }
18518     },
18519     
18520     remove: function() 
18521     {
18522         this.picker().remove();
18523     }
18524    
18525 });
18526
18527 Roo.apply(Roo.bootstrap.MonthField,  {
18528     
18529     content : {
18530         tag: 'tbody',
18531         cn: [
18532         {
18533             tag: 'tr',
18534             cn: [
18535             {
18536                 tag: 'td',
18537                 colspan: '7'
18538             }
18539             ]
18540         }
18541         ]
18542     },
18543     
18544     dates:{
18545         en: {
18546             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18547             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18548         }
18549     }
18550 });
18551
18552 Roo.apply(Roo.bootstrap.MonthField,  {
18553   
18554     template : {
18555         tag: 'div',
18556         cls: 'datepicker dropdown-menu roo-dynamic',
18557         cn: [
18558             {
18559                 tag: 'div',
18560                 cls: 'datepicker-months',
18561                 cn: [
18562                 {
18563                     tag: 'table',
18564                     cls: 'table-condensed',
18565                     cn:[
18566                         Roo.bootstrap.DateField.content
18567                     ]
18568                 }
18569                 ]
18570             }
18571         ]
18572     }
18573 });
18574
18575  
18576
18577  
18578  /*
18579  * - LGPL
18580  *
18581  * CheckBox
18582  * 
18583  */
18584
18585 /**
18586  * @class Roo.bootstrap.CheckBox
18587  * @extends Roo.bootstrap.Input
18588  * Bootstrap CheckBox class
18589  * 
18590  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18591  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18592  * @cfg {String} boxLabel The text that appears beside the checkbox
18593  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18594  * @cfg {Boolean} checked initnal the element
18595  * @cfg {Boolean} inline inline the element (default false)
18596  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18597  * 
18598  * @constructor
18599  * Create a new CheckBox
18600  * @param {Object} config The config object
18601  */
18602
18603 Roo.bootstrap.CheckBox = function(config){
18604     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18605    
18606     this.addEvents({
18607         /**
18608         * @event check
18609         * Fires when the element is checked or unchecked.
18610         * @param {Roo.bootstrap.CheckBox} this This input
18611         * @param {Boolean} checked The new checked value
18612         */
18613        check : true
18614     });
18615     
18616 };
18617
18618 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18619   
18620     inputType: 'checkbox',
18621     inputValue: 1,
18622     valueOff: 0,
18623     boxLabel: false,
18624     checked: false,
18625     weight : false,
18626     inline: false,
18627     
18628     getAutoCreate : function()
18629     {
18630         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18631         
18632         var id = Roo.id();
18633         
18634         var cfg = {};
18635         
18636         cfg.cls = 'form-group ' + this.inputType; //input-group
18637         
18638         if(this.inline){
18639             cfg.cls += ' ' + this.inputType + '-inline';
18640         }
18641         
18642         var input =  {
18643             tag: 'input',
18644             id : id,
18645             type : this.inputType,
18646             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18647             cls : 'roo-' + this.inputType, //'form-box',
18648             placeholder : this.placeholder || ''
18649             
18650         };
18651         
18652         if (this.weight) { // Validity check?
18653             cfg.cls += " " + this.inputType + "-" + this.weight;
18654         }
18655         
18656         if (this.disabled) {
18657             input.disabled=true;
18658         }
18659         
18660         if(this.checked){
18661             input.checked = this.checked;
18662         }
18663         
18664         if (this.name) {
18665             input.name = this.name;
18666         }
18667         
18668         if (this.size) {
18669             input.cls += ' input-' + this.size;
18670         }
18671         
18672         var settings=this;
18673         
18674         ['xs','sm','md','lg'].map(function(size){
18675             if (settings[size]) {
18676                 cfg.cls += ' col-' + size + '-' + settings[size];
18677             }
18678         });
18679         
18680         var inputblock = input;
18681          
18682         if (this.before || this.after) {
18683             
18684             inputblock = {
18685                 cls : 'input-group',
18686                 cn :  [] 
18687             };
18688             
18689             if (this.before) {
18690                 inputblock.cn.push({
18691                     tag :'span',
18692                     cls : 'input-group-addon',
18693                     html : this.before
18694                 });
18695             }
18696             
18697             inputblock.cn.push(input);
18698             
18699             if (this.after) {
18700                 inputblock.cn.push({
18701                     tag :'span',
18702                     cls : 'input-group-addon',
18703                     html : this.after
18704                 });
18705             }
18706             
18707         }
18708         
18709         if (align ==='left' && this.fieldLabel.length) {
18710 //                Roo.log("left and has label");
18711                 cfg.cn = [
18712                     
18713                     {
18714                         tag: 'label',
18715                         'for' :  id,
18716                         cls : 'control-label col-md-' + this.labelWidth,
18717                         html : this.fieldLabel
18718                         
18719                     },
18720                     {
18721                         cls : "col-md-" + (12 - this.labelWidth), 
18722                         cn: [
18723                             inputblock
18724                         ]
18725                     }
18726                     
18727                 ];
18728         } else if ( this.fieldLabel.length) {
18729 //                Roo.log(" label");
18730                 cfg.cn = [
18731                    
18732                     {
18733                         tag: this.boxLabel ? 'span' : 'label',
18734                         'for': id,
18735                         cls: 'control-label box-input-label',
18736                         //cls : 'input-group-addon',
18737                         html : this.fieldLabel
18738                         
18739                     },
18740                     
18741                     inputblock
18742                     
18743                 ];
18744
18745         } else {
18746             
18747 //                Roo.log(" no label && no align");
18748                 cfg.cn = [  inputblock ] ;
18749                 
18750                 
18751         }
18752         if(this.boxLabel){
18753              var boxLabelCfg = {
18754                 tag: 'label',
18755                 //'for': id, // box label is handled by onclick - so no for...
18756                 cls: 'box-label',
18757                 html: this.boxLabel
18758             };
18759             
18760             if(this.tooltip){
18761                 boxLabelCfg.tooltip = this.tooltip;
18762             }
18763              
18764             cfg.cn.push(boxLabelCfg);
18765         }
18766         
18767         
18768        
18769         return cfg;
18770         
18771     },
18772     
18773     /**
18774      * return the real input element.
18775      */
18776     inputEl: function ()
18777     {
18778         return this.el.select('input.roo-' + this.inputType,true).first();
18779     },
18780     
18781     labelEl: function()
18782     {
18783         return this.el.select('label.control-label',true).first();
18784     },
18785     /* depricated... */
18786     
18787     label: function()
18788     {
18789         return this.labelEl();
18790     },
18791     
18792     initEvents : function()
18793     {
18794 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18795         
18796         this.inputEl().on('click', this.onClick,  this);
18797         
18798         if (this.boxLabel) { 
18799             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18800         }
18801         
18802         this.startValue = this.getValue();
18803         
18804         if(this.groupId){
18805             Roo.bootstrap.CheckBox.register(this);
18806         }
18807     },
18808     
18809     onClick : function()
18810     {   
18811         this.setChecked(!this.checked);
18812     },
18813     
18814     setChecked : function(state,suppressEvent)
18815     {
18816         this.startValue = this.getValue();
18817         
18818         if(this.inputType == 'radio'){
18819             
18820             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18821                 e.dom.checked = false;
18822             });
18823             
18824             this.inputEl().dom.checked = true;
18825             
18826             this.inputEl().dom.value = this.inputValue;
18827             
18828             if(suppressEvent !== true){
18829                 this.fireEvent('check', this, true);
18830             }
18831             
18832             this.validate();
18833             
18834             return;
18835         }
18836         
18837         this.checked = state;
18838         
18839         this.inputEl().dom.checked = state;
18840         
18841         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18842         
18843         if(suppressEvent !== true){
18844             this.fireEvent('check', this, state);
18845         }
18846         
18847         this.validate();
18848     },
18849     
18850     getValue : function()
18851     {
18852         if(this.inputType == 'radio'){
18853             return this.getGroupValue();
18854         }
18855         
18856         return this.inputEl().getValue();
18857         
18858     },
18859     
18860     getGroupValue : function()
18861     {
18862         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18863             return '';
18864         }
18865         
18866         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18867     },
18868     
18869     setValue : function(v,suppressEvent)
18870     {
18871         if(this.inputType == 'radio'){
18872             this.setGroupValue(v, suppressEvent);
18873             return;
18874         }
18875         
18876         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18877         
18878         this.validate();
18879     },
18880     
18881     setGroupValue : function(v, suppressEvent)
18882     {
18883         this.startValue = this.getValue();
18884         
18885         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18886             e.dom.checked = false;
18887             
18888             if(e.dom.value == v){
18889                 e.dom.checked = true;
18890             }
18891         });
18892         
18893         if(suppressEvent !== true){
18894             this.fireEvent('check', this, true);
18895         }
18896
18897         this.validate();
18898         
18899         return;
18900     },
18901     
18902     validate : function()
18903     {
18904         if(
18905                 this.disabled || 
18906                 (this.inputType == 'radio' && this.validateRadio()) ||
18907                 (this.inputType == 'checkbox' && this.validateCheckbox())
18908         ){
18909             this.markValid();
18910             return true;
18911         }
18912         
18913         this.markInvalid();
18914         return false;
18915     },
18916     
18917     validateRadio : function()
18918     {
18919         var valid = false;
18920         
18921         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18922             if(!e.dom.checked){
18923                 return;
18924             }
18925             
18926             valid = true;
18927             
18928             return false;
18929         });
18930         
18931         return valid;
18932     },
18933     
18934     validateCheckbox : function()
18935     {
18936         if(!this.groupId){
18937             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18938         }
18939         
18940         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18941         
18942         if(!group){
18943             return false;
18944         }
18945         
18946         var r = false;
18947         
18948         for(var i in group){
18949             if(r){
18950                 break;
18951             }
18952             
18953             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18954         }
18955         
18956         return r;
18957     },
18958     
18959     /**
18960      * Mark this field as valid
18961      */
18962     markValid : function()
18963     {
18964         if(this.allowBlank){
18965             return;
18966         }
18967         
18968         var _this = this;
18969         
18970         this.fireEvent('valid', this);
18971         
18972         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18973         
18974         if(this.groupId){
18975             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18976         }
18977         
18978         if(label){
18979             label.markValid();
18980         }
18981         
18982         if(this.inputType == 'radio'){
18983             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18984                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18985                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18986             });
18987             
18988             return;
18989         }
18990         
18991         if(!this.groupId){
18992             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18993             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18994             return;
18995         }
18996         
18997         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18998             
18999         if(!group){
19000             return;
19001         }
19002         
19003         for(var i in group){
19004             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19005             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19006         }
19007     },
19008     
19009      /**
19010      * Mark this field as invalid
19011      * @param {String} msg The validation message
19012      */
19013     markInvalid : function(msg)
19014     {
19015         if(this.allowBlank){
19016             return;
19017         }
19018         
19019         var _this = this;
19020         
19021         this.fireEvent('invalid', this, msg);
19022         
19023         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19024         
19025         if(this.groupId){
19026             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19027         }
19028         
19029         if(label){
19030             label.markInvalid();
19031         }
19032             
19033         if(this.inputType == 'radio'){
19034             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19035                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19036                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19037             });
19038             
19039             return;
19040         }
19041         
19042         if(!this.groupId){
19043             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19044             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19045             return;
19046         }
19047         
19048         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19049         
19050         if(!group){
19051             return;
19052         }
19053         
19054         for(var i in group){
19055             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19056             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19057         }
19058         
19059     }
19060     
19061 });
19062
19063 Roo.apply(Roo.bootstrap.CheckBox, {
19064     
19065     groups: {},
19066     
19067      /**
19068     * register a CheckBox Group
19069     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19070     */
19071     register : function(checkbox)
19072     {
19073         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19074             this.groups[checkbox.groupId] = {};
19075         }
19076         
19077         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19078             return;
19079         }
19080         
19081         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19082         
19083     },
19084     /**
19085     * fetch a CheckBox Group based on the group ID
19086     * @param {string} the group ID
19087     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19088     */
19089     get: function(groupId) {
19090         if (typeof(this.groups[groupId]) == 'undefined') {
19091             return false;
19092         }
19093         
19094         return this.groups[groupId] ;
19095     }
19096     
19097     
19098 });
19099 /*
19100  * - LGPL
19101  *
19102  * Radio
19103  *
19104  *
19105  * not inline
19106  *<div class="radio">
19107   <label>
19108     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19109     Option one is this and that&mdash;be sure to include why it's great
19110   </label>
19111 </div>
19112  *
19113  *
19114  *inline
19115  *<span>
19116  *<label class="radio-inline">fieldLabel</label>
19117  *<label class="radio-inline">
19118   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19119 </label>
19120 <span>
19121  * 
19122  * 
19123  */
19124
19125 /**
19126  * @class Roo.bootstrap.Radio
19127  * @extends Roo.bootstrap.CheckBox
19128  * Bootstrap Radio class
19129
19130  * @constructor
19131  * Create a new Radio
19132  * @param {Object} config The config object
19133  */
19134
19135 Roo.bootstrap.Radio = function(config){
19136     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19137    
19138 };
19139
19140 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19141     
19142     inputType: 'radio',
19143     inputValue: '',
19144     valueOff: '',
19145     
19146     getAutoCreate : function()
19147     {
19148         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19149         align = align || 'left'; // default...
19150         
19151         
19152         
19153         var id = Roo.id();
19154         
19155         var cfg = {
19156                 tag : this.inline ? 'span' : 'div',
19157                 cls : '',
19158                 cn : []
19159         };
19160         
19161         var inline = this.inline ? ' radio-inline' : '';
19162         
19163         var lbl = {
19164                 tag: 'label' ,
19165                 // does not need for, as we wrap the input with it..
19166                 'for' : id,
19167                 cls : 'control-label box-label' + inline,
19168                 cn : []
19169         };
19170         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19171         
19172         var fieldLabel = {
19173             tag: 'label' ,
19174             //cls : 'control-label' + inline,
19175             html : this.fieldLabel,
19176             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19177         };
19178         
19179  
19180         
19181         
19182         var input =  {
19183             tag: 'input',
19184             id : id,
19185             type : this.inputType,
19186             //value : (!this.checked) ? this.valueOff : this.inputValue,
19187             value : this.inputValue,
19188             cls : 'roo-radio',
19189             placeholder : this.placeholder || '' // ?? needed????
19190             
19191         };
19192         if (this.weight) { // Validity check?
19193             input.cls += " radio-" + this.weight;
19194         }
19195         if (this.disabled) {
19196             input.disabled=true;
19197         }
19198         
19199         if(this.checked){
19200             input.checked = this.checked;
19201         }
19202         
19203         if (this.name) {
19204             input.name = this.name;
19205         }
19206         
19207         if (this.size) {
19208             input.cls += ' input-' + this.size;
19209         }
19210         
19211         //?? can span's inline have a width??
19212         
19213         var settings=this;
19214         ['xs','sm','md','lg'].map(function(size){
19215             if (settings[size]) {
19216                 cfg.cls += ' col-' + size + '-' + settings[size];
19217             }
19218         });
19219         
19220         var inputblock = input;
19221         
19222         if (this.before || this.after) {
19223             
19224             inputblock = {
19225                 cls : 'input-group',
19226                 tag : 'span',
19227                 cn :  [] 
19228             };
19229             if (this.before) {
19230                 inputblock.cn.push({
19231                     tag :'span',
19232                     cls : 'input-group-addon',
19233                     html : this.before
19234                 });
19235             }
19236             inputblock.cn.push(input);
19237             if (this.after) {
19238                 inputblock.cn.push({
19239                     tag :'span',
19240                     cls : 'input-group-addon',
19241                     html : this.after
19242                 });
19243             }
19244             
19245         };
19246         
19247         
19248         if (this.fieldLabel && this.fieldLabel.length) {
19249             cfg.cn.push(fieldLabel);
19250         }
19251        
19252         // normal bootstrap puts the input inside the label.
19253         // however with our styled version - it has to go after the input.
19254        
19255         //lbl.cn.push(inputblock);
19256         
19257         var lblwrap =  {
19258             tag: 'span',
19259             cls: 'radio' + inline,
19260             cn: [
19261                 inputblock,
19262                 lbl
19263             ]
19264         };
19265         
19266         cfg.cn.push( lblwrap);
19267         
19268         if(this.boxLabel){
19269             lbl.cn.push({
19270                 tag: 'span',
19271                 html: this.boxLabel
19272             })
19273         }
19274          
19275         
19276         return cfg;
19277         
19278     },
19279     
19280     initEvents : function()
19281     {
19282 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19283         
19284         this.inputEl().on('click', this.onClick,  this);
19285         if (this.boxLabel) {
19286             //Roo.log('find label');
19287             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19288         }
19289         
19290     },
19291     
19292     inputEl: function ()
19293     {
19294         return this.el.select('input.roo-radio',true).first();
19295     },
19296     onClick : function()
19297     {   
19298         Roo.log("click");
19299         this.setChecked(true);
19300     },
19301     
19302     setChecked : function(state,suppressEvent)
19303     {
19304         if(state){
19305             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19306                 v.dom.checked = false;
19307             });
19308         }
19309         Roo.log(this.inputEl().dom);
19310         this.checked = state;
19311         this.inputEl().dom.checked = state;
19312         
19313         if(suppressEvent !== true){
19314             this.fireEvent('check', this, state);
19315         }
19316         
19317         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19318         
19319     },
19320     
19321     getGroupValue : function()
19322     {
19323         var value = '';
19324         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19325             if(v.dom.checked == true){
19326                 value = v.dom.value;
19327             }
19328         });
19329         
19330         return value;
19331     },
19332     
19333     /**
19334      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19335      * @return {Mixed} value The field value
19336      */
19337     getValue : function(){
19338         return this.getGroupValue();
19339     }
19340     
19341 });
19342
19343  
19344 //<script type="text/javascript">
19345
19346 /*
19347  * Based  Ext JS Library 1.1.1
19348  * Copyright(c) 2006-2007, Ext JS, LLC.
19349  * LGPL
19350  *
19351  */
19352  
19353 /**
19354  * @class Roo.HtmlEditorCore
19355  * @extends Roo.Component
19356  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19357  *
19358  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19359  */
19360
19361 Roo.HtmlEditorCore = function(config){
19362     
19363     
19364     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19365     
19366     
19367     this.addEvents({
19368         /**
19369          * @event initialize
19370          * Fires when the editor is fully initialized (including the iframe)
19371          * @param {Roo.HtmlEditorCore} this
19372          */
19373         initialize: true,
19374         /**
19375          * @event activate
19376          * Fires when the editor is first receives the focus. Any insertion must wait
19377          * until after this event.
19378          * @param {Roo.HtmlEditorCore} this
19379          */
19380         activate: true,
19381          /**
19382          * @event beforesync
19383          * Fires before the textarea is updated with content from the editor iframe. Return false
19384          * to cancel the sync.
19385          * @param {Roo.HtmlEditorCore} this
19386          * @param {String} html
19387          */
19388         beforesync: true,
19389          /**
19390          * @event beforepush
19391          * Fires before the iframe editor is updated with content from the textarea. Return false
19392          * to cancel the push.
19393          * @param {Roo.HtmlEditorCore} this
19394          * @param {String} html
19395          */
19396         beforepush: true,
19397          /**
19398          * @event sync
19399          * Fires when the textarea is updated with content from the editor iframe.
19400          * @param {Roo.HtmlEditorCore} this
19401          * @param {String} html
19402          */
19403         sync: true,
19404          /**
19405          * @event push
19406          * Fires when the iframe editor is updated with content from the textarea.
19407          * @param {Roo.HtmlEditorCore} this
19408          * @param {String} html
19409          */
19410         push: true,
19411         
19412         /**
19413          * @event editorevent
19414          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19415          * @param {Roo.HtmlEditorCore} this
19416          */
19417         editorevent: true
19418         
19419     });
19420     
19421     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19422     
19423     // defaults : white / black...
19424     this.applyBlacklists();
19425     
19426     
19427     
19428 };
19429
19430
19431 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19432
19433
19434      /**
19435      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19436      */
19437     
19438     owner : false,
19439     
19440      /**
19441      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19442      *                        Roo.resizable.
19443      */
19444     resizable : false,
19445      /**
19446      * @cfg {Number} height (in pixels)
19447      */   
19448     height: 300,
19449    /**
19450      * @cfg {Number} width (in pixels)
19451      */   
19452     width: 500,
19453     
19454     /**
19455      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19456      * 
19457      */
19458     stylesheets: false,
19459     
19460     // id of frame..
19461     frameId: false,
19462     
19463     // private properties
19464     validationEvent : false,
19465     deferHeight: true,
19466     initialized : false,
19467     activated : false,
19468     sourceEditMode : false,
19469     onFocus : Roo.emptyFn,
19470     iframePad:3,
19471     hideMode:'offsets',
19472     
19473     clearUp: true,
19474     
19475     // blacklist + whitelisted elements..
19476     black: false,
19477     white: false,
19478      
19479     
19480
19481     /**
19482      * Protected method that will not generally be called directly. It
19483      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19484      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19485      */
19486     getDocMarkup : function(){
19487         // body styles..
19488         var st = '';
19489         
19490         // inherit styels from page...?? 
19491         if (this.stylesheets === false) {
19492             
19493             Roo.get(document.head).select('style').each(function(node) {
19494                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19495             });
19496             
19497             Roo.get(document.head).select('link').each(function(node) { 
19498                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19499             });
19500             
19501         } else if (!this.stylesheets.length) {
19502                 // simple..
19503                 st = '<style type="text/css">' +
19504                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19505                    '</style>';
19506         } else { 
19507             
19508         }
19509         
19510         st +=  '<style type="text/css">' +
19511             'IMG { cursor: pointer } ' +
19512         '</style>';
19513
19514         
19515         return '<html><head>' + st  +
19516             //<style type="text/css">' +
19517             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19518             //'</style>' +
19519             ' </head><body class="roo-htmleditor-body"></body></html>';
19520     },
19521
19522     // private
19523     onRender : function(ct, position)
19524     {
19525         var _t = this;
19526         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19527         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19528         
19529         
19530         this.el.dom.style.border = '0 none';
19531         this.el.dom.setAttribute('tabIndex', -1);
19532         this.el.addClass('x-hidden hide');
19533         
19534         
19535         
19536         if(Roo.isIE){ // fix IE 1px bogus margin
19537             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19538         }
19539        
19540         
19541         this.frameId = Roo.id();
19542         
19543          
19544         
19545         var iframe = this.owner.wrap.createChild({
19546             tag: 'iframe',
19547             cls: 'form-control', // bootstrap..
19548             id: this.frameId,
19549             name: this.frameId,
19550             frameBorder : 'no',
19551             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19552         }, this.el
19553         );
19554         
19555         
19556         this.iframe = iframe.dom;
19557
19558          this.assignDocWin();
19559         
19560         this.doc.designMode = 'on';
19561        
19562         this.doc.open();
19563         this.doc.write(this.getDocMarkup());
19564         this.doc.close();
19565
19566         
19567         var task = { // must defer to wait for browser to be ready
19568             run : function(){
19569                 //console.log("run task?" + this.doc.readyState);
19570                 this.assignDocWin();
19571                 if(this.doc.body || this.doc.readyState == 'complete'){
19572                     try {
19573                         this.doc.designMode="on";
19574                     } catch (e) {
19575                         return;
19576                     }
19577                     Roo.TaskMgr.stop(task);
19578                     this.initEditor.defer(10, this);
19579                 }
19580             },
19581             interval : 10,
19582             duration: 10000,
19583             scope: this
19584         };
19585         Roo.TaskMgr.start(task);
19586
19587     },
19588
19589     // private
19590     onResize : function(w, h)
19591     {
19592          Roo.log('resize: ' +w + ',' + h );
19593         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19594         if(!this.iframe){
19595             return;
19596         }
19597         if(typeof w == 'number'){
19598             
19599             this.iframe.style.width = w + 'px';
19600         }
19601         if(typeof h == 'number'){
19602             
19603             this.iframe.style.height = h + 'px';
19604             if(this.doc){
19605                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19606             }
19607         }
19608         
19609     },
19610
19611     /**
19612      * Toggles the editor between standard and source edit mode.
19613      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19614      */
19615     toggleSourceEdit : function(sourceEditMode){
19616         
19617         this.sourceEditMode = sourceEditMode === true;
19618         
19619         if(this.sourceEditMode){
19620  
19621             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19622             
19623         }else{
19624             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19625             //this.iframe.className = '';
19626             this.deferFocus();
19627         }
19628         //this.setSize(this.owner.wrap.getSize());
19629         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19630     },
19631
19632     
19633   
19634
19635     /**
19636      * Protected method that will not generally be called directly. If you need/want
19637      * custom HTML cleanup, this is the method you should override.
19638      * @param {String} html The HTML to be cleaned
19639      * return {String} The cleaned HTML
19640      */
19641     cleanHtml : function(html){
19642         html = String(html);
19643         if(html.length > 5){
19644             if(Roo.isSafari){ // strip safari nonsense
19645                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19646             }
19647         }
19648         if(html == '&nbsp;'){
19649             html = '';
19650         }
19651         return html;
19652     },
19653
19654     /**
19655      * HTML Editor -> Textarea
19656      * Protected method that will not generally be called directly. Syncs the contents
19657      * of the editor iframe with the textarea.
19658      */
19659     syncValue : function(){
19660         if(this.initialized){
19661             var bd = (this.doc.body || this.doc.documentElement);
19662             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19663             var html = bd.innerHTML;
19664             if(Roo.isSafari){
19665                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19666                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19667                 if(m && m[1]){
19668                     html = '<div style="'+m[0]+'">' + html + '</div>';
19669                 }
19670             }
19671             html = this.cleanHtml(html);
19672             // fix up the special chars.. normaly like back quotes in word...
19673             // however we do not want to do this with chinese..
19674             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19675                 var cc = b.charCodeAt();
19676                 if (
19677                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19678                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19679                     (cc >= 0xf900 && cc < 0xfb00 )
19680                 ) {
19681                         return b;
19682                 }
19683                 return "&#"+cc+";" 
19684             });
19685             if(this.owner.fireEvent('beforesync', this, html) !== false){
19686                 this.el.dom.value = html;
19687                 this.owner.fireEvent('sync', this, html);
19688             }
19689         }
19690     },
19691
19692     /**
19693      * Protected method that will not generally be called directly. Pushes the value of the textarea
19694      * into the iframe editor.
19695      */
19696     pushValue : function(){
19697         if(this.initialized){
19698             var v = this.el.dom.value.trim();
19699             
19700 //            if(v.length < 1){
19701 //                v = '&#160;';
19702 //            }
19703             
19704             if(this.owner.fireEvent('beforepush', this, v) !== false){
19705                 var d = (this.doc.body || this.doc.documentElement);
19706                 d.innerHTML = v;
19707                 this.cleanUpPaste();
19708                 this.el.dom.value = d.innerHTML;
19709                 this.owner.fireEvent('push', this, v);
19710             }
19711         }
19712     },
19713
19714     // private
19715     deferFocus : function(){
19716         this.focus.defer(10, this);
19717     },
19718
19719     // doc'ed in Field
19720     focus : function(){
19721         if(this.win && !this.sourceEditMode){
19722             this.win.focus();
19723         }else{
19724             this.el.focus();
19725         }
19726     },
19727     
19728     assignDocWin: function()
19729     {
19730         var iframe = this.iframe;
19731         
19732          if(Roo.isIE){
19733             this.doc = iframe.contentWindow.document;
19734             this.win = iframe.contentWindow;
19735         } else {
19736 //            if (!Roo.get(this.frameId)) {
19737 //                return;
19738 //            }
19739 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19740 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19741             
19742             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19743                 return;
19744             }
19745             
19746             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19747             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19748         }
19749     },
19750     
19751     // private
19752     initEditor : function(){
19753         //console.log("INIT EDITOR");
19754         this.assignDocWin();
19755         
19756         
19757         
19758         this.doc.designMode="on";
19759         this.doc.open();
19760         this.doc.write(this.getDocMarkup());
19761         this.doc.close();
19762         
19763         var dbody = (this.doc.body || this.doc.documentElement);
19764         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19765         // this copies styles from the containing element into thsi one..
19766         // not sure why we need all of this..
19767         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19768         
19769         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19770         //ss['background-attachment'] = 'fixed'; // w3c
19771         dbody.bgProperties = 'fixed'; // ie
19772         //Roo.DomHelper.applyStyles(dbody, ss);
19773         Roo.EventManager.on(this.doc, {
19774             //'mousedown': this.onEditorEvent,
19775             'mouseup': this.onEditorEvent,
19776             'dblclick': this.onEditorEvent,
19777             'click': this.onEditorEvent,
19778             'keyup': this.onEditorEvent,
19779             buffer:100,
19780             scope: this
19781         });
19782         if(Roo.isGecko){
19783             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19784         }
19785         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19786             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19787         }
19788         this.initialized = true;
19789
19790         this.owner.fireEvent('initialize', this);
19791         this.pushValue();
19792     },
19793
19794     // private
19795     onDestroy : function(){
19796         
19797         
19798         
19799         if(this.rendered){
19800             
19801             //for (var i =0; i < this.toolbars.length;i++) {
19802             //    // fixme - ask toolbars for heights?
19803             //    this.toolbars[i].onDestroy();
19804            // }
19805             
19806             //this.wrap.dom.innerHTML = '';
19807             //this.wrap.remove();
19808         }
19809     },
19810
19811     // private
19812     onFirstFocus : function(){
19813         
19814         this.assignDocWin();
19815         
19816         
19817         this.activated = true;
19818          
19819     
19820         if(Roo.isGecko){ // prevent silly gecko errors
19821             this.win.focus();
19822             var s = this.win.getSelection();
19823             if(!s.focusNode || s.focusNode.nodeType != 3){
19824                 var r = s.getRangeAt(0);
19825                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19826                 r.collapse(true);
19827                 this.deferFocus();
19828             }
19829             try{
19830                 this.execCmd('useCSS', true);
19831                 this.execCmd('styleWithCSS', false);
19832             }catch(e){}
19833         }
19834         this.owner.fireEvent('activate', this);
19835     },
19836
19837     // private
19838     adjustFont: function(btn){
19839         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19840         //if(Roo.isSafari){ // safari
19841         //    adjust *= 2;
19842        // }
19843         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19844         if(Roo.isSafari){ // safari
19845             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19846             v =  (v < 10) ? 10 : v;
19847             v =  (v > 48) ? 48 : v;
19848             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19849             
19850         }
19851         
19852         
19853         v = Math.max(1, v+adjust);
19854         
19855         this.execCmd('FontSize', v  );
19856     },
19857
19858     onEditorEvent : function(e)
19859     {
19860         this.owner.fireEvent('editorevent', this, e);
19861       //  this.updateToolbar();
19862         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19863     },
19864
19865     insertTag : function(tg)
19866     {
19867         // could be a bit smarter... -> wrap the current selected tRoo..
19868         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19869             
19870             range = this.createRange(this.getSelection());
19871             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19872             wrappingNode.appendChild(range.extractContents());
19873             range.insertNode(wrappingNode);
19874
19875             return;
19876             
19877             
19878             
19879         }
19880         this.execCmd("formatblock",   tg);
19881         
19882     },
19883     
19884     insertText : function(txt)
19885     {
19886         
19887         
19888         var range = this.createRange();
19889         range.deleteContents();
19890                //alert(Sender.getAttribute('label'));
19891                
19892         range.insertNode(this.doc.createTextNode(txt));
19893     } ,
19894     
19895      
19896
19897     /**
19898      * Executes a Midas editor command on the editor document and performs necessary focus and
19899      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19900      * @param {String} cmd The Midas command
19901      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19902      */
19903     relayCmd : function(cmd, value){
19904         this.win.focus();
19905         this.execCmd(cmd, value);
19906         this.owner.fireEvent('editorevent', this);
19907         //this.updateToolbar();
19908         this.owner.deferFocus();
19909     },
19910
19911     /**
19912      * Executes a Midas editor command directly on the editor document.
19913      * For visual commands, you should use {@link #relayCmd} instead.
19914      * <b>This should only be called after the editor is initialized.</b>
19915      * @param {String} cmd The Midas command
19916      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19917      */
19918     execCmd : function(cmd, value){
19919         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19920         this.syncValue();
19921     },
19922  
19923  
19924    
19925     /**
19926      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19927      * to insert tRoo.
19928      * @param {String} text | dom node.. 
19929      */
19930     insertAtCursor : function(text)
19931     {
19932         
19933         
19934         
19935         if(!this.activated){
19936             return;
19937         }
19938         /*
19939         if(Roo.isIE){
19940             this.win.focus();
19941             var r = this.doc.selection.createRange();
19942             if(r){
19943                 r.collapse(true);
19944                 r.pasteHTML(text);
19945                 this.syncValue();
19946                 this.deferFocus();
19947             
19948             }
19949             return;
19950         }
19951         */
19952         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19953             this.win.focus();
19954             
19955             
19956             // from jquery ui (MIT licenced)
19957             var range, node;
19958             var win = this.win;
19959             
19960             if (win.getSelection && win.getSelection().getRangeAt) {
19961                 range = win.getSelection().getRangeAt(0);
19962                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19963                 range.insertNode(node);
19964             } else if (win.document.selection && win.document.selection.createRange) {
19965                 // no firefox support
19966                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19967                 win.document.selection.createRange().pasteHTML(txt);
19968             } else {
19969                 // no firefox support
19970                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19971                 this.execCmd('InsertHTML', txt);
19972             } 
19973             
19974             this.syncValue();
19975             
19976             this.deferFocus();
19977         }
19978     },
19979  // private
19980     mozKeyPress : function(e){
19981         if(e.ctrlKey){
19982             var c = e.getCharCode(), cmd;
19983           
19984             if(c > 0){
19985                 c = String.fromCharCode(c).toLowerCase();
19986                 switch(c){
19987                     case 'b':
19988                         cmd = 'bold';
19989                         break;
19990                     case 'i':
19991                         cmd = 'italic';
19992                         break;
19993                     
19994                     case 'u':
19995                         cmd = 'underline';
19996                         break;
19997                     
19998                     case 'v':
19999                         this.cleanUpPaste.defer(100, this);
20000                         return;
20001                         
20002                 }
20003                 if(cmd){
20004                     this.win.focus();
20005                     this.execCmd(cmd);
20006                     this.deferFocus();
20007                     e.preventDefault();
20008                 }
20009                 
20010             }
20011         }
20012     },
20013
20014     // private
20015     fixKeys : function(){ // load time branching for fastest keydown performance
20016         if(Roo.isIE){
20017             return function(e){
20018                 var k = e.getKey(), r;
20019                 if(k == e.TAB){
20020                     e.stopEvent();
20021                     r = this.doc.selection.createRange();
20022                     if(r){
20023                         r.collapse(true);
20024                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20025                         this.deferFocus();
20026                     }
20027                     return;
20028                 }
20029                 
20030                 if(k == e.ENTER){
20031                     r = this.doc.selection.createRange();
20032                     if(r){
20033                         var target = r.parentElement();
20034                         if(!target || target.tagName.toLowerCase() != 'li'){
20035                             e.stopEvent();
20036                             r.pasteHTML('<br />');
20037                             r.collapse(false);
20038                             r.select();
20039                         }
20040                     }
20041                 }
20042                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20043                     this.cleanUpPaste.defer(100, this);
20044                     return;
20045                 }
20046                 
20047                 
20048             };
20049         }else if(Roo.isOpera){
20050             return function(e){
20051                 var k = e.getKey();
20052                 if(k == e.TAB){
20053                     e.stopEvent();
20054                     this.win.focus();
20055                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20056                     this.deferFocus();
20057                 }
20058                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20059                     this.cleanUpPaste.defer(100, this);
20060                     return;
20061                 }
20062                 
20063             };
20064         }else if(Roo.isSafari){
20065             return function(e){
20066                 var k = e.getKey();
20067                 
20068                 if(k == e.TAB){
20069                     e.stopEvent();
20070                     this.execCmd('InsertText','\t');
20071                     this.deferFocus();
20072                     return;
20073                 }
20074                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20075                     this.cleanUpPaste.defer(100, this);
20076                     return;
20077                 }
20078                 
20079              };
20080         }
20081     }(),
20082     
20083     getAllAncestors: function()
20084     {
20085         var p = this.getSelectedNode();
20086         var a = [];
20087         if (!p) {
20088             a.push(p); // push blank onto stack..
20089             p = this.getParentElement();
20090         }
20091         
20092         
20093         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20094             a.push(p);
20095             p = p.parentNode;
20096         }
20097         a.push(this.doc.body);
20098         return a;
20099     },
20100     lastSel : false,
20101     lastSelNode : false,
20102     
20103     
20104     getSelection : function() 
20105     {
20106         this.assignDocWin();
20107         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20108     },
20109     
20110     getSelectedNode: function() 
20111     {
20112         // this may only work on Gecko!!!
20113         
20114         // should we cache this!!!!
20115         
20116         
20117         
20118          
20119         var range = this.createRange(this.getSelection()).cloneRange();
20120         
20121         if (Roo.isIE) {
20122             var parent = range.parentElement();
20123             while (true) {
20124                 var testRange = range.duplicate();
20125                 testRange.moveToElementText(parent);
20126                 if (testRange.inRange(range)) {
20127                     break;
20128                 }
20129                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20130                     break;
20131                 }
20132                 parent = parent.parentElement;
20133             }
20134             return parent;
20135         }
20136         
20137         // is ancestor a text element.
20138         var ac =  range.commonAncestorContainer;
20139         if (ac.nodeType == 3) {
20140             ac = ac.parentNode;
20141         }
20142         
20143         var ar = ac.childNodes;
20144          
20145         var nodes = [];
20146         var other_nodes = [];
20147         var has_other_nodes = false;
20148         for (var i=0;i<ar.length;i++) {
20149             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20150                 continue;
20151             }
20152             // fullly contained node.
20153             
20154             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20155                 nodes.push(ar[i]);
20156                 continue;
20157             }
20158             
20159             // probably selected..
20160             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20161                 other_nodes.push(ar[i]);
20162                 continue;
20163             }
20164             // outer..
20165             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20166                 continue;
20167             }
20168             
20169             
20170             has_other_nodes = true;
20171         }
20172         if (!nodes.length && other_nodes.length) {
20173             nodes= other_nodes;
20174         }
20175         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20176             return false;
20177         }
20178         
20179         return nodes[0];
20180     },
20181     createRange: function(sel)
20182     {
20183         // this has strange effects when using with 
20184         // top toolbar - not sure if it's a great idea.
20185         //this.editor.contentWindow.focus();
20186         if (typeof sel != "undefined") {
20187             try {
20188                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20189             } catch(e) {
20190                 return this.doc.createRange();
20191             }
20192         } else {
20193             return this.doc.createRange();
20194         }
20195     },
20196     getParentElement: function()
20197     {
20198         
20199         this.assignDocWin();
20200         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20201         
20202         var range = this.createRange(sel);
20203          
20204         try {
20205             var p = range.commonAncestorContainer;
20206             while (p.nodeType == 3) { // text node
20207                 p = p.parentNode;
20208             }
20209             return p;
20210         } catch (e) {
20211             return null;
20212         }
20213     
20214     },
20215     /***
20216      *
20217      * Range intersection.. the hard stuff...
20218      *  '-1' = before
20219      *  '0' = hits..
20220      *  '1' = after.
20221      *         [ -- selected range --- ]
20222      *   [fail]                        [fail]
20223      *
20224      *    basically..
20225      *      if end is before start or  hits it. fail.
20226      *      if start is after end or hits it fail.
20227      *
20228      *   if either hits (but other is outside. - then it's not 
20229      *   
20230      *    
20231      **/
20232     
20233     
20234     // @see http://www.thismuchiknow.co.uk/?p=64.
20235     rangeIntersectsNode : function(range, node)
20236     {
20237         var nodeRange = node.ownerDocument.createRange();
20238         try {
20239             nodeRange.selectNode(node);
20240         } catch (e) {
20241             nodeRange.selectNodeContents(node);
20242         }
20243     
20244         var rangeStartRange = range.cloneRange();
20245         rangeStartRange.collapse(true);
20246     
20247         var rangeEndRange = range.cloneRange();
20248         rangeEndRange.collapse(false);
20249     
20250         var nodeStartRange = nodeRange.cloneRange();
20251         nodeStartRange.collapse(true);
20252     
20253         var nodeEndRange = nodeRange.cloneRange();
20254         nodeEndRange.collapse(false);
20255     
20256         return rangeStartRange.compareBoundaryPoints(
20257                  Range.START_TO_START, nodeEndRange) == -1 &&
20258                rangeEndRange.compareBoundaryPoints(
20259                  Range.START_TO_START, nodeStartRange) == 1;
20260         
20261          
20262     },
20263     rangeCompareNode : function(range, node)
20264     {
20265         var nodeRange = node.ownerDocument.createRange();
20266         try {
20267             nodeRange.selectNode(node);
20268         } catch (e) {
20269             nodeRange.selectNodeContents(node);
20270         }
20271         
20272         
20273         range.collapse(true);
20274     
20275         nodeRange.collapse(true);
20276      
20277         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20278         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20279          
20280         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20281         
20282         var nodeIsBefore   =  ss == 1;
20283         var nodeIsAfter    = ee == -1;
20284         
20285         if (nodeIsBefore && nodeIsAfter) {
20286             return 0; // outer
20287         }
20288         if (!nodeIsBefore && nodeIsAfter) {
20289             return 1; //right trailed.
20290         }
20291         
20292         if (nodeIsBefore && !nodeIsAfter) {
20293             return 2;  // left trailed.
20294         }
20295         // fully contined.
20296         return 3;
20297     },
20298
20299     // private? - in a new class?
20300     cleanUpPaste :  function()
20301     {
20302         // cleans up the whole document..
20303         Roo.log('cleanuppaste');
20304         
20305         this.cleanUpChildren(this.doc.body);
20306         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20307         if (clean != this.doc.body.innerHTML) {
20308             this.doc.body.innerHTML = clean;
20309         }
20310         
20311     },
20312     
20313     cleanWordChars : function(input) {// change the chars to hex code
20314         var he = Roo.HtmlEditorCore;
20315         
20316         var output = input;
20317         Roo.each(he.swapCodes, function(sw) { 
20318             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20319             
20320             output = output.replace(swapper, sw[1]);
20321         });
20322         
20323         return output;
20324     },
20325     
20326     
20327     cleanUpChildren : function (n)
20328     {
20329         if (!n.childNodes.length) {
20330             return;
20331         }
20332         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20333            this.cleanUpChild(n.childNodes[i]);
20334         }
20335     },
20336     
20337     
20338         
20339     
20340     cleanUpChild : function (node)
20341     {
20342         var ed = this;
20343         //console.log(node);
20344         if (node.nodeName == "#text") {
20345             // clean up silly Windows -- stuff?
20346             return; 
20347         }
20348         if (node.nodeName == "#comment") {
20349             node.parentNode.removeChild(node);
20350             // clean up silly Windows -- stuff?
20351             return; 
20352         }
20353         var lcname = node.tagName.toLowerCase();
20354         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20355         // whitelist of tags..
20356         
20357         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20358             // remove node.
20359             node.parentNode.removeChild(node);
20360             return;
20361             
20362         }
20363         
20364         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20365         
20366         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20367         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20368         
20369         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20370         //    remove_keep_children = true;
20371         //}
20372         
20373         if (remove_keep_children) {
20374             this.cleanUpChildren(node);
20375             // inserts everything just before this node...
20376             while (node.childNodes.length) {
20377                 var cn = node.childNodes[0];
20378                 node.removeChild(cn);
20379                 node.parentNode.insertBefore(cn, node);
20380             }
20381             node.parentNode.removeChild(node);
20382             return;
20383         }
20384         
20385         if (!node.attributes || !node.attributes.length) {
20386             this.cleanUpChildren(node);
20387             return;
20388         }
20389         
20390         function cleanAttr(n,v)
20391         {
20392             
20393             if (v.match(/^\./) || v.match(/^\//)) {
20394                 return;
20395             }
20396             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20397                 return;
20398             }
20399             if (v.match(/^#/)) {
20400                 return;
20401             }
20402 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20403             node.removeAttribute(n);
20404             
20405         }
20406         
20407         var cwhite = this.cwhite;
20408         var cblack = this.cblack;
20409             
20410         function cleanStyle(n,v)
20411         {
20412             if (v.match(/expression/)) { //XSS?? should we even bother..
20413                 node.removeAttribute(n);
20414                 return;
20415             }
20416             
20417             var parts = v.split(/;/);
20418             var clean = [];
20419             
20420             Roo.each(parts, function(p) {
20421                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20422                 if (!p.length) {
20423                     return true;
20424                 }
20425                 var l = p.split(':').shift().replace(/\s+/g,'');
20426                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20427                 
20428                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20429 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20430                     //node.removeAttribute(n);
20431                     return true;
20432                 }
20433                 //Roo.log()
20434                 // only allow 'c whitelisted system attributes'
20435                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20436 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20437                     //node.removeAttribute(n);
20438                     return true;
20439                 }
20440                 
20441                 
20442                  
20443                 
20444                 clean.push(p);
20445                 return true;
20446             });
20447             if (clean.length) { 
20448                 node.setAttribute(n, clean.join(';'));
20449             } else {
20450                 node.removeAttribute(n);
20451             }
20452             
20453         }
20454         
20455         
20456         for (var i = node.attributes.length-1; i > -1 ; i--) {
20457             var a = node.attributes[i];
20458             //console.log(a);
20459             
20460             if (a.name.toLowerCase().substr(0,2)=='on')  {
20461                 node.removeAttribute(a.name);
20462                 continue;
20463             }
20464             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20465                 node.removeAttribute(a.name);
20466                 continue;
20467             }
20468             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20469                 cleanAttr(a.name,a.value); // fixme..
20470                 continue;
20471             }
20472             if (a.name == 'style') {
20473                 cleanStyle(a.name,a.value);
20474                 continue;
20475             }
20476             /// clean up MS crap..
20477             // tecnically this should be a list of valid class'es..
20478             
20479             
20480             if (a.name == 'class') {
20481                 if (a.value.match(/^Mso/)) {
20482                     node.className = '';
20483                 }
20484                 
20485                 if (a.value.match(/body/)) {
20486                     node.className = '';
20487                 }
20488                 continue;
20489             }
20490             
20491             // style cleanup!?
20492             // class cleanup?
20493             
20494         }
20495         
20496         
20497         this.cleanUpChildren(node);
20498         
20499         
20500     },
20501     
20502     /**
20503      * Clean up MS wordisms...
20504      */
20505     cleanWord : function(node)
20506     {
20507         
20508         
20509         if (!node) {
20510             this.cleanWord(this.doc.body);
20511             return;
20512         }
20513         if (node.nodeName == "#text") {
20514             // clean up silly Windows -- stuff?
20515             return; 
20516         }
20517         if (node.nodeName == "#comment") {
20518             node.parentNode.removeChild(node);
20519             // clean up silly Windows -- stuff?
20520             return; 
20521         }
20522         
20523         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20524             node.parentNode.removeChild(node);
20525             return;
20526         }
20527         
20528         // remove - but keep children..
20529         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20530             while (node.childNodes.length) {
20531                 var cn = node.childNodes[0];
20532                 node.removeChild(cn);
20533                 node.parentNode.insertBefore(cn, node);
20534             }
20535             node.parentNode.removeChild(node);
20536             this.iterateChildren(node, this.cleanWord);
20537             return;
20538         }
20539         // clean styles
20540         if (node.className.length) {
20541             
20542             var cn = node.className.split(/\W+/);
20543             var cna = [];
20544             Roo.each(cn, function(cls) {
20545                 if (cls.match(/Mso[a-zA-Z]+/)) {
20546                     return;
20547                 }
20548                 cna.push(cls);
20549             });
20550             node.className = cna.length ? cna.join(' ') : '';
20551             if (!cna.length) {
20552                 node.removeAttribute("class");
20553             }
20554         }
20555         
20556         if (node.hasAttribute("lang")) {
20557             node.removeAttribute("lang");
20558         }
20559         
20560         if (node.hasAttribute("style")) {
20561             
20562             var styles = node.getAttribute("style").split(";");
20563             var nstyle = [];
20564             Roo.each(styles, function(s) {
20565                 if (!s.match(/:/)) {
20566                     return;
20567                 }
20568                 var kv = s.split(":");
20569                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20570                     return;
20571                 }
20572                 // what ever is left... we allow.
20573                 nstyle.push(s);
20574             });
20575             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20576             if (!nstyle.length) {
20577                 node.removeAttribute('style');
20578             }
20579         }
20580         this.iterateChildren(node, this.cleanWord);
20581         
20582         
20583         
20584     },
20585     /**
20586      * iterateChildren of a Node, calling fn each time, using this as the scole..
20587      * @param {DomNode} node node to iterate children of.
20588      * @param {Function} fn method of this class to call on each item.
20589      */
20590     iterateChildren : function(node, fn)
20591     {
20592         if (!node.childNodes.length) {
20593                 return;
20594         }
20595         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20596            fn.call(this, node.childNodes[i])
20597         }
20598     },
20599     
20600     
20601     /**
20602      * cleanTableWidths.
20603      *
20604      * Quite often pasting from word etc.. results in tables with column and widths.
20605      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20606      *
20607      */
20608     cleanTableWidths : function(node)
20609     {
20610          
20611          
20612         if (!node) {
20613             this.cleanTableWidths(this.doc.body);
20614             return;
20615         }
20616         
20617         // ignore list...
20618         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20619             return; 
20620         }
20621         Roo.log(node.tagName);
20622         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20623             this.iterateChildren(node, this.cleanTableWidths);
20624             return;
20625         }
20626         if (node.hasAttribute('width')) {
20627             node.removeAttribute('width');
20628         }
20629         
20630          
20631         if (node.hasAttribute("style")) {
20632             // pretty basic...
20633             
20634             var styles = node.getAttribute("style").split(";");
20635             var nstyle = [];
20636             Roo.each(styles, function(s) {
20637                 if (!s.match(/:/)) {
20638                     return;
20639                 }
20640                 var kv = s.split(":");
20641                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20642                     return;
20643                 }
20644                 // what ever is left... we allow.
20645                 nstyle.push(s);
20646             });
20647             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20648             if (!nstyle.length) {
20649                 node.removeAttribute('style');
20650             }
20651         }
20652         
20653         this.iterateChildren(node, this.cleanTableWidths);
20654         
20655         
20656     },
20657     
20658     
20659     
20660     
20661     domToHTML : function(currentElement, depth, nopadtext) {
20662         
20663         depth = depth || 0;
20664         nopadtext = nopadtext || false;
20665     
20666         if (!currentElement) {
20667             return this.domToHTML(this.doc.body);
20668         }
20669         
20670         //Roo.log(currentElement);
20671         var j;
20672         var allText = false;
20673         var nodeName = currentElement.nodeName;
20674         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20675         
20676         if  (nodeName == '#text') {
20677             
20678             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20679         }
20680         
20681         
20682         var ret = '';
20683         if (nodeName != 'BODY') {
20684              
20685             var i = 0;
20686             // Prints the node tagName, such as <A>, <IMG>, etc
20687             if (tagName) {
20688                 var attr = [];
20689                 for(i = 0; i < currentElement.attributes.length;i++) {
20690                     // quoting?
20691                     var aname = currentElement.attributes.item(i).name;
20692                     if (!currentElement.attributes.item(i).value.length) {
20693                         continue;
20694                     }
20695                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20696                 }
20697                 
20698                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20699             } 
20700             else {
20701                 
20702                 // eack
20703             }
20704         } else {
20705             tagName = false;
20706         }
20707         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20708             return ret;
20709         }
20710         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20711             nopadtext = true;
20712         }
20713         
20714         
20715         // Traverse the tree
20716         i = 0;
20717         var currentElementChild = currentElement.childNodes.item(i);
20718         var allText = true;
20719         var innerHTML  = '';
20720         lastnode = '';
20721         while (currentElementChild) {
20722             // Formatting code (indent the tree so it looks nice on the screen)
20723             var nopad = nopadtext;
20724             if (lastnode == 'SPAN') {
20725                 nopad  = true;
20726             }
20727             // text
20728             if  (currentElementChild.nodeName == '#text') {
20729                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20730                 toadd = nopadtext ? toadd : toadd.trim();
20731                 if (!nopad && toadd.length > 80) {
20732                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20733                 }
20734                 innerHTML  += toadd;
20735                 
20736                 i++;
20737                 currentElementChild = currentElement.childNodes.item(i);
20738                 lastNode = '';
20739                 continue;
20740             }
20741             allText = false;
20742             
20743             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20744                 
20745             // Recursively traverse the tree structure of the child node
20746             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20747             lastnode = currentElementChild.nodeName;
20748             i++;
20749             currentElementChild=currentElement.childNodes.item(i);
20750         }
20751         
20752         ret += innerHTML;
20753         
20754         if (!allText) {
20755                 // The remaining code is mostly for formatting the tree
20756             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20757         }
20758         
20759         
20760         if (tagName) {
20761             ret+= "</"+tagName+">";
20762         }
20763         return ret;
20764         
20765     },
20766         
20767     applyBlacklists : function()
20768     {
20769         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20770         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20771         
20772         this.white = [];
20773         this.black = [];
20774         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20775             if (b.indexOf(tag) > -1) {
20776                 return;
20777             }
20778             this.white.push(tag);
20779             
20780         }, this);
20781         
20782         Roo.each(w, function(tag) {
20783             if (b.indexOf(tag) > -1) {
20784                 return;
20785             }
20786             if (this.white.indexOf(tag) > -1) {
20787                 return;
20788             }
20789             this.white.push(tag);
20790             
20791         }, this);
20792         
20793         
20794         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20795             if (w.indexOf(tag) > -1) {
20796                 return;
20797             }
20798             this.black.push(tag);
20799             
20800         }, this);
20801         
20802         Roo.each(b, function(tag) {
20803             if (w.indexOf(tag) > -1) {
20804                 return;
20805             }
20806             if (this.black.indexOf(tag) > -1) {
20807                 return;
20808             }
20809             this.black.push(tag);
20810             
20811         }, this);
20812         
20813         
20814         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20815         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20816         
20817         this.cwhite = [];
20818         this.cblack = [];
20819         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20820             if (b.indexOf(tag) > -1) {
20821                 return;
20822             }
20823             this.cwhite.push(tag);
20824             
20825         }, this);
20826         
20827         Roo.each(w, function(tag) {
20828             if (b.indexOf(tag) > -1) {
20829                 return;
20830             }
20831             if (this.cwhite.indexOf(tag) > -1) {
20832                 return;
20833             }
20834             this.cwhite.push(tag);
20835             
20836         }, this);
20837         
20838         
20839         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20840             if (w.indexOf(tag) > -1) {
20841                 return;
20842             }
20843             this.cblack.push(tag);
20844             
20845         }, this);
20846         
20847         Roo.each(b, function(tag) {
20848             if (w.indexOf(tag) > -1) {
20849                 return;
20850             }
20851             if (this.cblack.indexOf(tag) > -1) {
20852                 return;
20853             }
20854             this.cblack.push(tag);
20855             
20856         }, this);
20857     },
20858     
20859     setStylesheets : function(stylesheets)
20860     {
20861         if(typeof(stylesheets) == 'string'){
20862             Roo.get(this.iframe.contentDocument.head).createChild({
20863                 tag : 'link',
20864                 rel : 'stylesheet',
20865                 type : 'text/css',
20866                 href : stylesheets
20867             });
20868             
20869             return;
20870         }
20871         var _this = this;
20872      
20873         Roo.each(stylesheets, function(s) {
20874             if(!s.length){
20875                 return;
20876             }
20877             
20878             Roo.get(_this.iframe.contentDocument.head).createChild({
20879                 tag : 'link',
20880                 rel : 'stylesheet',
20881                 type : 'text/css',
20882                 href : s
20883             });
20884         });
20885
20886         
20887     },
20888     
20889     removeStylesheets : function()
20890     {
20891         var _this = this;
20892         
20893         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20894             s.remove();
20895         });
20896     }
20897     
20898     // hide stuff that is not compatible
20899     /**
20900      * @event blur
20901      * @hide
20902      */
20903     /**
20904      * @event change
20905      * @hide
20906      */
20907     /**
20908      * @event focus
20909      * @hide
20910      */
20911     /**
20912      * @event specialkey
20913      * @hide
20914      */
20915     /**
20916      * @cfg {String} fieldClass @hide
20917      */
20918     /**
20919      * @cfg {String} focusClass @hide
20920      */
20921     /**
20922      * @cfg {String} autoCreate @hide
20923      */
20924     /**
20925      * @cfg {String} inputType @hide
20926      */
20927     /**
20928      * @cfg {String} invalidClass @hide
20929      */
20930     /**
20931      * @cfg {String} invalidText @hide
20932      */
20933     /**
20934      * @cfg {String} msgFx @hide
20935      */
20936     /**
20937      * @cfg {String} validateOnBlur @hide
20938      */
20939 });
20940
20941 Roo.HtmlEditorCore.white = [
20942         'area', 'br', 'img', 'input', 'hr', 'wbr',
20943         
20944        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20945        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20946        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20947        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20948        'table',   'ul',         'xmp', 
20949        
20950        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20951       'thead',   'tr', 
20952      
20953       'dir', 'menu', 'ol', 'ul', 'dl',
20954        
20955       'embed',  'object'
20956 ];
20957
20958
20959 Roo.HtmlEditorCore.black = [
20960     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20961         'applet', // 
20962         'base',   'basefont', 'bgsound', 'blink',  'body', 
20963         'frame',  'frameset', 'head',    'html',   'ilayer', 
20964         'iframe', 'layer',  'link',     'meta',    'object',   
20965         'script', 'style' ,'title',  'xml' // clean later..
20966 ];
20967 Roo.HtmlEditorCore.clean = [
20968     'script', 'style', 'title', 'xml'
20969 ];
20970 Roo.HtmlEditorCore.remove = [
20971     'font'
20972 ];
20973 // attributes..
20974
20975 Roo.HtmlEditorCore.ablack = [
20976     'on'
20977 ];
20978     
20979 Roo.HtmlEditorCore.aclean = [ 
20980     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20981 ];
20982
20983 // protocols..
20984 Roo.HtmlEditorCore.pwhite= [
20985         'http',  'https',  'mailto'
20986 ];
20987
20988 // white listed style attributes.
20989 Roo.HtmlEditorCore.cwhite= [
20990       //  'text-align', /// default is to allow most things..
20991       
20992          
20993 //        'font-size'//??
20994 ];
20995
20996 // black listed style attributes.
20997 Roo.HtmlEditorCore.cblack= [
20998       //  'font-size' -- this can be set by the project 
20999 ];
21000
21001
21002 Roo.HtmlEditorCore.swapCodes   =[ 
21003     [    8211, "--" ], 
21004     [    8212, "--" ], 
21005     [    8216,  "'" ],  
21006     [    8217, "'" ],  
21007     [    8220, '"' ],  
21008     [    8221, '"' ],  
21009     [    8226, "*" ],  
21010     [    8230, "..." ]
21011 ]; 
21012
21013     /*
21014  * - LGPL
21015  *
21016  * HtmlEditor
21017  * 
21018  */
21019
21020 /**
21021  * @class Roo.bootstrap.HtmlEditor
21022  * @extends Roo.bootstrap.TextArea
21023  * Bootstrap HtmlEditor class
21024
21025  * @constructor
21026  * Create a new HtmlEditor
21027  * @param {Object} config The config object
21028  */
21029
21030 Roo.bootstrap.HtmlEditor = function(config){
21031     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21032     if (!this.toolbars) {
21033         this.toolbars = [];
21034     }
21035     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21036     this.addEvents({
21037             /**
21038              * @event initialize
21039              * Fires when the editor is fully initialized (including the iframe)
21040              * @param {HtmlEditor} this
21041              */
21042             initialize: true,
21043             /**
21044              * @event activate
21045              * Fires when the editor is first receives the focus. Any insertion must wait
21046              * until after this event.
21047              * @param {HtmlEditor} this
21048              */
21049             activate: true,
21050              /**
21051              * @event beforesync
21052              * Fires before the textarea is updated with content from the editor iframe. Return false
21053              * to cancel the sync.
21054              * @param {HtmlEditor} this
21055              * @param {String} html
21056              */
21057             beforesync: true,
21058              /**
21059              * @event beforepush
21060              * Fires before the iframe editor is updated with content from the textarea. Return false
21061              * to cancel the push.
21062              * @param {HtmlEditor} this
21063              * @param {String} html
21064              */
21065             beforepush: true,
21066              /**
21067              * @event sync
21068              * Fires when the textarea is updated with content from the editor iframe.
21069              * @param {HtmlEditor} this
21070              * @param {String} html
21071              */
21072             sync: true,
21073              /**
21074              * @event push
21075              * Fires when the iframe editor is updated with content from the textarea.
21076              * @param {HtmlEditor} this
21077              * @param {String} html
21078              */
21079             push: true,
21080              /**
21081              * @event editmodechange
21082              * Fires when the editor switches edit modes
21083              * @param {HtmlEditor} this
21084              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21085              */
21086             editmodechange: true,
21087             /**
21088              * @event editorevent
21089              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21090              * @param {HtmlEditor} this
21091              */
21092             editorevent: true,
21093             /**
21094              * @event firstfocus
21095              * Fires when on first focus - needed by toolbars..
21096              * @param {HtmlEditor} this
21097              */
21098             firstfocus: true,
21099             /**
21100              * @event autosave
21101              * Auto save the htmlEditor value as a file into Events
21102              * @param {HtmlEditor} this
21103              */
21104             autosave: true,
21105             /**
21106              * @event savedpreview
21107              * preview the saved version of htmlEditor
21108              * @param {HtmlEditor} this
21109              */
21110             savedpreview: true
21111         });
21112 };
21113
21114
21115 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21116     
21117     
21118       /**
21119      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21120      */
21121     toolbars : false,
21122    
21123      /**
21124      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21125      *                        Roo.resizable.
21126      */
21127     resizable : false,
21128      /**
21129      * @cfg {Number} height (in pixels)
21130      */   
21131     height: 300,
21132    /**
21133      * @cfg {Number} width (in pixels)
21134      */   
21135     width: false,
21136     
21137     /**
21138      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21139      * 
21140      */
21141     stylesheets: false,
21142     
21143     // id of frame..
21144     frameId: false,
21145     
21146     // private properties
21147     validationEvent : false,
21148     deferHeight: true,
21149     initialized : false,
21150     activated : false,
21151     
21152     onFocus : Roo.emptyFn,
21153     iframePad:3,
21154     hideMode:'offsets',
21155     
21156     
21157     tbContainer : false,
21158     
21159     toolbarContainer :function() {
21160         return this.wrap.select('.x-html-editor-tb',true).first();
21161     },
21162
21163     /**
21164      * Protected method that will not generally be called directly. It
21165      * is called when the editor creates its toolbar. Override this method if you need to
21166      * add custom toolbar buttons.
21167      * @param {HtmlEditor} editor
21168      */
21169     createToolbar : function(){
21170         
21171         Roo.log("create toolbars");
21172         
21173         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21174         this.toolbars[0].render(this.toolbarContainer());
21175         
21176         return;
21177         
21178 //        if (!editor.toolbars || !editor.toolbars.length) {
21179 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21180 //        }
21181 //        
21182 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21183 //            editor.toolbars[i] = Roo.factory(
21184 //                    typeof(editor.toolbars[i]) == 'string' ?
21185 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21186 //                Roo.bootstrap.HtmlEditor);
21187 //            editor.toolbars[i].init(editor);
21188 //        }
21189     },
21190
21191      
21192     // private
21193     onRender : function(ct, position)
21194     {
21195        // Roo.log("Call onRender: " + this.xtype);
21196         var _t = this;
21197         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21198       
21199         this.wrap = this.inputEl().wrap({
21200             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21201         });
21202         
21203         this.editorcore.onRender(ct, position);
21204          
21205         if (this.resizable) {
21206             this.resizeEl = new Roo.Resizable(this.wrap, {
21207                 pinned : true,
21208                 wrap: true,
21209                 dynamic : true,
21210                 minHeight : this.height,
21211                 height: this.height,
21212                 handles : this.resizable,
21213                 width: this.width,
21214                 listeners : {
21215                     resize : function(r, w, h) {
21216                         _t.onResize(w,h); // -something
21217                     }
21218                 }
21219             });
21220             
21221         }
21222         this.createToolbar(this);
21223        
21224         
21225         if(!this.width && this.resizable){
21226             this.setSize(this.wrap.getSize());
21227         }
21228         if (this.resizeEl) {
21229             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21230             // should trigger onReize..
21231         }
21232         
21233     },
21234
21235     // private
21236     onResize : function(w, h)
21237     {
21238         Roo.log('resize: ' +w + ',' + h );
21239         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21240         var ew = false;
21241         var eh = false;
21242         
21243         if(this.inputEl() ){
21244             if(typeof w == 'number'){
21245                 var aw = w - this.wrap.getFrameWidth('lr');
21246                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21247                 ew = aw;
21248             }
21249             if(typeof h == 'number'){
21250                  var tbh = -11;  // fixme it needs to tool bar size!
21251                 for (var i =0; i < this.toolbars.length;i++) {
21252                     // fixme - ask toolbars for heights?
21253                     tbh += this.toolbars[i].el.getHeight();
21254                     //if (this.toolbars[i].footer) {
21255                     //    tbh += this.toolbars[i].footer.el.getHeight();
21256                     //}
21257                 }
21258               
21259                 
21260                 
21261                 
21262                 
21263                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21264                 ah -= 5; // knock a few pixes off for look..
21265                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21266                 var eh = ah;
21267             }
21268         }
21269         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21270         this.editorcore.onResize(ew,eh);
21271         
21272     },
21273
21274     /**
21275      * Toggles the editor between standard and source edit mode.
21276      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21277      */
21278     toggleSourceEdit : function(sourceEditMode)
21279     {
21280         this.editorcore.toggleSourceEdit(sourceEditMode);
21281         
21282         if(this.editorcore.sourceEditMode){
21283             Roo.log('editor - showing textarea');
21284             
21285 //            Roo.log('in');
21286 //            Roo.log(this.syncValue());
21287             this.syncValue();
21288             this.inputEl().removeClass(['hide', 'x-hidden']);
21289             this.inputEl().dom.removeAttribute('tabIndex');
21290             this.inputEl().focus();
21291         }else{
21292             Roo.log('editor - hiding textarea');
21293 //            Roo.log('out')
21294 //            Roo.log(this.pushValue()); 
21295             this.pushValue();
21296             
21297             this.inputEl().addClass(['hide', 'x-hidden']);
21298             this.inputEl().dom.setAttribute('tabIndex', -1);
21299             //this.deferFocus();
21300         }
21301          
21302         if(this.resizable){
21303             this.setSize(this.wrap.getSize());
21304         }
21305         
21306         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21307     },
21308  
21309     // private (for BoxComponent)
21310     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21311
21312     // private (for BoxComponent)
21313     getResizeEl : function(){
21314         return this.wrap;
21315     },
21316
21317     // private (for BoxComponent)
21318     getPositionEl : function(){
21319         return this.wrap;
21320     },
21321
21322     // private
21323     initEvents : function(){
21324         this.originalValue = this.getValue();
21325     },
21326
21327 //    /**
21328 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21329 //     * @method
21330 //     */
21331 //    markInvalid : Roo.emptyFn,
21332 //    /**
21333 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21334 //     * @method
21335 //     */
21336 //    clearInvalid : Roo.emptyFn,
21337
21338     setValue : function(v){
21339         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21340         this.editorcore.pushValue();
21341     },
21342
21343      
21344     // private
21345     deferFocus : function(){
21346         this.focus.defer(10, this);
21347     },
21348
21349     // doc'ed in Field
21350     focus : function(){
21351         this.editorcore.focus();
21352         
21353     },
21354       
21355
21356     // private
21357     onDestroy : function(){
21358         
21359         
21360         
21361         if(this.rendered){
21362             
21363             for (var i =0; i < this.toolbars.length;i++) {
21364                 // fixme - ask toolbars for heights?
21365                 this.toolbars[i].onDestroy();
21366             }
21367             
21368             this.wrap.dom.innerHTML = '';
21369             this.wrap.remove();
21370         }
21371     },
21372
21373     // private
21374     onFirstFocus : function(){
21375         //Roo.log("onFirstFocus");
21376         this.editorcore.onFirstFocus();
21377          for (var i =0; i < this.toolbars.length;i++) {
21378             this.toolbars[i].onFirstFocus();
21379         }
21380         
21381     },
21382     
21383     // private
21384     syncValue : function()
21385     {   
21386         this.editorcore.syncValue();
21387     },
21388     
21389     pushValue : function()
21390     {   
21391         this.editorcore.pushValue();
21392     }
21393      
21394     
21395     // hide stuff that is not compatible
21396     /**
21397      * @event blur
21398      * @hide
21399      */
21400     /**
21401      * @event change
21402      * @hide
21403      */
21404     /**
21405      * @event focus
21406      * @hide
21407      */
21408     /**
21409      * @event specialkey
21410      * @hide
21411      */
21412     /**
21413      * @cfg {String} fieldClass @hide
21414      */
21415     /**
21416      * @cfg {String} focusClass @hide
21417      */
21418     /**
21419      * @cfg {String} autoCreate @hide
21420      */
21421     /**
21422      * @cfg {String} inputType @hide
21423      */
21424     /**
21425      * @cfg {String} invalidClass @hide
21426      */
21427     /**
21428      * @cfg {String} invalidText @hide
21429      */
21430     /**
21431      * @cfg {String} msgFx @hide
21432      */
21433     /**
21434      * @cfg {String} validateOnBlur @hide
21435      */
21436 });
21437  
21438     
21439    
21440    
21441    
21442       
21443 Roo.namespace('Roo.bootstrap.htmleditor');
21444 /**
21445  * @class Roo.bootstrap.HtmlEditorToolbar1
21446  * Basic Toolbar
21447  * 
21448  * Usage:
21449  *
21450  new Roo.bootstrap.HtmlEditor({
21451     ....
21452     toolbars : [
21453         new Roo.bootstrap.HtmlEditorToolbar1({
21454             disable : { fonts: 1 , format: 1, ..., ... , ...],
21455             btns : [ .... ]
21456         })
21457     }
21458      
21459  * 
21460  * @cfg {Object} disable List of elements to disable..
21461  * @cfg {Array} btns List of additional buttons.
21462  * 
21463  * 
21464  * NEEDS Extra CSS? 
21465  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21466  */
21467  
21468 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21469 {
21470     
21471     Roo.apply(this, config);
21472     
21473     // default disabled, based on 'good practice'..
21474     this.disable = this.disable || {};
21475     Roo.applyIf(this.disable, {
21476         fontSize : true,
21477         colors : true,
21478         specialElements : true
21479     });
21480     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21481     
21482     this.editor = config.editor;
21483     this.editorcore = config.editor.editorcore;
21484     
21485     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21486     
21487     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21488     // dont call parent... till later.
21489 }
21490 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21491      
21492     bar : true,
21493     
21494     editor : false,
21495     editorcore : false,
21496     
21497     
21498     formats : [
21499         "p" ,  
21500         "h1","h2","h3","h4","h5","h6", 
21501         "pre", "code", 
21502         "abbr", "acronym", "address", "cite", "samp", "var",
21503         'div','span'
21504     ],
21505     
21506     onRender : function(ct, position)
21507     {
21508        // Roo.log("Call onRender: " + this.xtype);
21509         
21510        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21511        Roo.log(this.el);
21512        this.el.dom.style.marginBottom = '0';
21513        var _this = this;
21514        var editorcore = this.editorcore;
21515        var editor= this.editor;
21516        
21517        var children = [];
21518        var btn = function(id,cmd , toggle, handler){
21519        
21520             var  event = toggle ? 'toggle' : 'click';
21521        
21522             var a = {
21523                 size : 'sm',
21524                 xtype: 'Button',
21525                 xns: Roo.bootstrap,
21526                 glyphicon : id,
21527                 cmd : id || cmd,
21528                 enableToggle:toggle !== false,
21529                 //html : 'submit'
21530                 pressed : toggle ? false : null,
21531                 listeners : {}
21532             };
21533             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21534                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21535             };
21536             children.push(a);
21537             return a;
21538        }
21539         
21540         var style = {
21541                 xtype: 'Button',
21542                 size : 'sm',
21543                 xns: Roo.bootstrap,
21544                 glyphicon : 'font',
21545                 //html : 'submit'
21546                 menu : {
21547                     xtype: 'Menu',
21548                     xns: Roo.bootstrap,
21549                     items:  []
21550                 }
21551         };
21552         Roo.each(this.formats, function(f) {
21553             style.menu.items.push({
21554                 xtype :'MenuItem',
21555                 xns: Roo.bootstrap,
21556                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21557                 tagname : f,
21558                 listeners : {
21559                     click : function()
21560                     {
21561                         editorcore.insertTag(this.tagname);
21562                         editor.focus();
21563                     }
21564                 }
21565                 
21566             });
21567         });
21568          children.push(style);   
21569             
21570             
21571         btn('bold',false,true);
21572         btn('italic',false,true);
21573         btn('align-left', 'justifyleft',true);
21574         btn('align-center', 'justifycenter',true);
21575         btn('align-right' , 'justifyright',true);
21576         btn('link', false, false, function(btn) {
21577             //Roo.log("create link?");
21578             var url = prompt(this.createLinkText, this.defaultLinkValue);
21579             if(url && url != 'http:/'+'/'){
21580                 this.editorcore.relayCmd('createlink', url);
21581             }
21582         }),
21583         btn('list','insertunorderedlist',true);
21584         btn('pencil', false,true, function(btn){
21585                 Roo.log(this);
21586                 
21587                 this.toggleSourceEdit(btn.pressed);
21588         });
21589         /*
21590         var cog = {
21591                 xtype: 'Button',
21592                 size : 'sm',
21593                 xns: Roo.bootstrap,
21594                 glyphicon : 'cog',
21595                 //html : 'submit'
21596                 menu : {
21597                     xtype: 'Menu',
21598                     xns: Roo.bootstrap,
21599                     items:  []
21600                 }
21601         };
21602         
21603         cog.menu.items.push({
21604             xtype :'MenuItem',
21605             xns: Roo.bootstrap,
21606             html : Clean styles,
21607             tagname : f,
21608             listeners : {
21609                 click : function()
21610                 {
21611                     editorcore.insertTag(this.tagname);
21612                     editor.focus();
21613                 }
21614             }
21615             
21616         });
21617        */
21618         
21619          
21620        this.xtype = 'NavSimplebar';
21621         
21622         for(var i=0;i< children.length;i++) {
21623             
21624             this.buttons.add(this.addxtypeChild(children[i]));
21625             
21626         }
21627         
21628         editor.on('editorevent', this.updateToolbar, this);
21629     },
21630     onBtnClick : function(id)
21631     {
21632        this.editorcore.relayCmd(id);
21633        this.editorcore.focus();
21634     },
21635     
21636     /**
21637      * Protected method that will not generally be called directly. It triggers
21638      * a toolbar update by reading the markup state of the current selection in the editor.
21639      */
21640     updateToolbar: function(){
21641
21642         if(!this.editorcore.activated){
21643             this.editor.onFirstFocus(); // is this neeed?
21644             return;
21645         }
21646
21647         var btns = this.buttons; 
21648         var doc = this.editorcore.doc;
21649         btns.get('bold').setActive(doc.queryCommandState('bold'));
21650         btns.get('italic').setActive(doc.queryCommandState('italic'));
21651         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21652         
21653         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21654         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21655         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21656         
21657         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21658         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21659          /*
21660         
21661         var ans = this.editorcore.getAllAncestors();
21662         if (this.formatCombo) {
21663             
21664             
21665             var store = this.formatCombo.store;
21666             this.formatCombo.setValue("");
21667             for (var i =0; i < ans.length;i++) {
21668                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21669                     // select it..
21670                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21671                     break;
21672                 }
21673             }
21674         }
21675         
21676         
21677         
21678         // hides menus... - so this cant be on a menu...
21679         Roo.bootstrap.MenuMgr.hideAll();
21680         */
21681         Roo.bootstrap.MenuMgr.hideAll();
21682         //this.editorsyncValue();
21683     },
21684     onFirstFocus: function() {
21685         this.buttons.each(function(item){
21686            item.enable();
21687         });
21688     },
21689     toggleSourceEdit : function(sourceEditMode){
21690         
21691           
21692         if(sourceEditMode){
21693             Roo.log("disabling buttons");
21694            this.buttons.each( function(item){
21695                 if(item.cmd != 'pencil'){
21696                     item.disable();
21697                 }
21698             });
21699           
21700         }else{
21701             Roo.log("enabling buttons");
21702             if(this.editorcore.initialized){
21703                 this.buttons.each( function(item){
21704                     item.enable();
21705                 });
21706             }
21707             
21708         }
21709         Roo.log("calling toggole on editor");
21710         // tell the editor that it's been pressed..
21711         this.editor.toggleSourceEdit(sourceEditMode);
21712        
21713     }
21714 });
21715
21716
21717
21718
21719
21720 /**
21721  * @class Roo.bootstrap.Table.AbstractSelectionModel
21722  * @extends Roo.util.Observable
21723  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21724  * implemented by descendant classes.  This class should not be directly instantiated.
21725  * @constructor
21726  */
21727 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21728     this.locked = false;
21729     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21730 };
21731
21732
21733 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21734     /** @ignore Called by the grid automatically. Do not call directly. */
21735     init : function(grid){
21736         this.grid = grid;
21737         this.initEvents();
21738     },
21739
21740     /**
21741      * Locks the selections.
21742      */
21743     lock : function(){
21744         this.locked = true;
21745     },
21746
21747     /**
21748      * Unlocks the selections.
21749      */
21750     unlock : function(){
21751         this.locked = false;
21752     },
21753
21754     /**
21755      * Returns true if the selections are locked.
21756      * @return {Boolean}
21757      */
21758     isLocked : function(){
21759         return this.locked;
21760     }
21761 });
21762 /**
21763  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21764  * @class Roo.bootstrap.Table.RowSelectionModel
21765  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21766  * It supports multiple selections and keyboard selection/navigation. 
21767  * @constructor
21768  * @param {Object} config
21769  */
21770
21771 Roo.bootstrap.Table.RowSelectionModel = function(config){
21772     Roo.apply(this, config);
21773     this.selections = new Roo.util.MixedCollection(false, function(o){
21774         return o.id;
21775     });
21776
21777     this.last = false;
21778     this.lastActive = false;
21779
21780     this.addEvents({
21781         /**
21782              * @event selectionchange
21783              * Fires when the selection changes
21784              * @param {SelectionModel} this
21785              */
21786             "selectionchange" : true,
21787         /**
21788              * @event afterselectionchange
21789              * Fires after the selection changes (eg. by key press or clicking)
21790              * @param {SelectionModel} this
21791              */
21792             "afterselectionchange" : true,
21793         /**
21794              * @event beforerowselect
21795              * Fires when a row is selected being selected, return false to cancel.
21796              * @param {SelectionModel} this
21797              * @param {Number} rowIndex The selected index
21798              * @param {Boolean} keepExisting False if other selections will be cleared
21799              */
21800             "beforerowselect" : true,
21801         /**
21802              * @event rowselect
21803              * Fires when a row is selected.
21804              * @param {SelectionModel} this
21805              * @param {Number} rowIndex The selected index
21806              * @param {Roo.data.Record} r The record
21807              */
21808             "rowselect" : true,
21809         /**
21810              * @event rowdeselect
21811              * Fires when a row is deselected.
21812              * @param {SelectionModel} this
21813              * @param {Number} rowIndex The selected index
21814              */
21815         "rowdeselect" : true
21816     });
21817     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21818     this.locked = false;
21819 };
21820
21821 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21822     /**
21823      * @cfg {Boolean} singleSelect
21824      * True to allow selection of only one row at a time (defaults to false)
21825      */
21826     singleSelect : false,
21827
21828     // private
21829     initEvents : function(){
21830
21831         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21832             this.grid.on("mousedown", this.handleMouseDown, this);
21833         }else{ // allow click to work like normal
21834             this.grid.on("rowclick", this.handleDragableRowClick, this);
21835         }
21836
21837         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21838             "up" : function(e){
21839                 if(!e.shiftKey){
21840                     this.selectPrevious(e.shiftKey);
21841                 }else if(this.last !== false && this.lastActive !== false){
21842                     var last = this.last;
21843                     this.selectRange(this.last,  this.lastActive-1);
21844                     this.grid.getView().focusRow(this.lastActive);
21845                     if(last !== false){
21846                         this.last = last;
21847                     }
21848                 }else{
21849                     this.selectFirstRow();
21850                 }
21851                 this.fireEvent("afterselectionchange", this);
21852             },
21853             "down" : function(e){
21854                 if(!e.shiftKey){
21855                     this.selectNext(e.shiftKey);
21856                 }else if(this.last !== false && this.lastActive !== false){
21857                     var last = this.last;
21858                     this.selectRange(this.last,  this.lastActive+1);
21859                     this.grid.getView().focusRow(this.lastActive);
21860                     if(last !== false){
21861                         this.last = last;
21862                     }
21863                 }else{
21864                     this.selectFirstRow();
21865                 }
21866                 this.fireEvent("afterselectionchange", this);
21867             },
21868             scope: this
21869         });
21870
21871         var view = this.grid.view;
21872         view.on("refresh", this.onRefresh, this);
21873         view.on("rowupdated", this.onRowUpdated, this);
21874         view.on("rowremoved", this.onRemove, this);
21875     },
21876
21877     // private
21878     onRefresh : function(){
21879         var ds = this.grid.dataSource, i, v = this.grid.view;
21880         var s = this.selections;
21881         s.each(function(r){
21882             if((i = ds.indexOfId(r.id)) != -1){
21883                 v.onRowSelect(i);
21884             }else{
21885                 s.remove(r);
21886             }
21887         });
21888     },
21889
21890     // private
21891     onRemove : function(v, index, r){
21892         this.selections.remove(r);
21893     },
21894
21895     // private
21896     onRowUpdated : function(v, index, r){
21897         if(this.isSelected(r)){
21898             v.onRowSelect(index);
21899         }
21900     },
21901
21902     /**
21903      * Select records.
21904      * @param {Array} records The records to select
21905      * @param {Boolean} keepExisting (optional) True to keep existing selections
21906      */
21907     selectRecords : function(records, keepExisting){
21908         if(!keepExisting){
21909             this.clearSelections();
21910         }
21911         var ds = this.grid.dataSource;
21912         for(var i = 0, len = records.length; i < len; i++){
21913             this.selectRow(ds.indexOf(records[i]), true);
21914         }
21915     },
21916
21917     /**
21918      * Gets the number of selected rows.
21919      * @return {Number}
21920      */
21921     getCount : function(){
21922         return this.selections.length;
21923     },
21924
21925     /**
21926      * Selects the first row in the grid.
21927      */
21928     selectFirstRow : function(){
21929         this.selectRow(0);
21930     },
21931
21932     /**
21933      * Select the last row.
21934      * @param {Boolean} keepExisting (optional) True to keep existing selections
21935      */
21936     selectLastRow : function(keepExisting){
21937         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21938     },
21939
21940     /**
21941      * Selects the row immediately following the last selected row.
21942      * @param {Boolean} keepExisting (optional) True to keep existing selections
21943      */
21944     selectNext : function(keepExisting){
21945         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21946             this.selectRow(this.last+1, keepExisting);
21947             this.grid.getView().focusRow(this.last);
21948         }
21949     },
21950
21951     /**
21952      * Selects the row that precedes the last selected row.
21953      * @param {Boolean} keepExisting (optional) True to keep existing selections
21954      */
21955     selectPrevious : function(keepExisting){
21956         if(this.last){
21957             this.selectRow(this.last-1, keepExisting);
21958             this.grid.getView().focusRow(this.last);
21959         }
21960     },
21961
21962     /**
21963      * Returns the selected records
21964      * @return {Array} Array of selected records
21965      */
21966     getSelections : function(){
21967         return [].concat(this.selections.items);
21968     },
21969
21970     /**
21971      * Returns the first selected record.
21972      * @return {Record}
21973      */
21974     getSelected : function(){
21975         return this.selections.itemAt(0);
21976     },
21977
21978
21979     /**
21980      * Clears all selections.
21981      */
21982     clearSelections : function(fast){
21983         if(this.locked) {
21984             return;
21985         }
21986         if(fast !== true){
21987             var ds = this.grid.dataSource;
21988             var s = this.selections;
21989             s.each(function(r){
21990                 this.deselectRow(ds.indexOfId(r.id));
21991             }, this);
21992             s.clear();
21993         }else{
21994             this.selections.clear();
21995         }
21996         this.last = false;
21997     },
21998
21999
22000     /**
22001      * Selects all rows.
22002      */
22003     selectAll : function(){
22004         if(this.locked) {
22005             return;
22006         }
22007         this.selections.clear();
22008         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22009             this.selectRow(i, true);
22010         }
22011     },
22012
22013     /**
22014      * Returns True if there is a selection.
22015      * @return {Boolean}
22016      */
22017     hasSelection : function(){
22018         return this.selections.length > 0;
22019     },
22020
22021     /**
22022      * Returns True if the specified row is selected.
22023      * @param {Number/Record} record The record or index of the record to check
22024      * @return {Boolean}
22025      */
22026     isSelected : function(index){
22027         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22028         return (r && this.selections.key(r.id) ? true : false);
22029     },
22030
22031     /**
22032      * Returns True if the specified record id is selected.
22033      * @param {String} id The id of record to check
22034      * @return {Boolean}
22035      */
22036     isIdSelected : function(id){
22037         return (this.selections.key(id) ? true : false);
22038     },
22039
22040     // private
22041     handleMouseDown : function(e, t){
22042         var view = this.grid.getView(), rowIndex;
22043         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22044             return;
22045         };
22046         if(e.shiftKey && this.last !== false){
22047             var last = this.last;
22048             this.selectRange(last, rowIndex, e.ctrlKey);
22049             this.last = last; // reset the last
22050             view.focusRow(rowIndex);
22051         }else{
22052             var isSelected = this.isSelected(rowIndex);
22053             if(e.button !== 0 && isSelected){
22054                 view.focusRow(rowIndex);
22055             }else if(e.ctrlKey && isSelected){
22056                 this.deselectRow(rowIndex);
22057             }else if(!isSelected){
22058                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22059                 view.focusRow(rowIndex);
22060             }
22061         }
22062         this.fireEvent("afterselectionchange", this);
22063     },
22064     // private
22065     handleDragableRowClick :  function(grid, rowIndex, e) 
22066     {
22067         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22068             this.selectRow(rowIndex, false);
22069             grid.view.focusRow(rowIndex);
22070              this.fireEvent("afterselectionchange", this);
22071         }
22072     },
22073     
22074     /**
22075      * Selects multiple rows.
22076      * @param {Array} rows Array of the indexes of the row to select
22077      * @param {Boolean} keepExisting (optional) True to keep existing selections
22078      */
22079     selectRows : function(rows, keepExisting){
22080         if(!keepExisting){
22081             this.clearSelections();
22082         }
22083         for(var i = 0, len = rows.length; i < len; i++){
22084             this.selectRow(rows[i], true);
22085         }
22086     },
22087
22088     /**
22089      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22090      * @param {Number} startRow The index of the first row in the range
22091      * @param {Number} endRow The index of the last row in the range
22092      * @param {Boolean} keepExisting (optional) True to retain existing selections
22093      */
22094     selectRange : function(startRow, endRow, keepExisting){
22095         if(this.locked) {
22096             return;
22097         }
22098         if(!keepExisting){
22099             this.clearSelections();
22100         }
22101         if(startRow <= endRow){
22102             for(var i = startRow; i <= endRow; i++){
22103                 this.selectRow(i, true);
22104             }
22105         }else{
22106             for(var i = startRow; i >= endRow; i--){
22107                 this.selectRow(i, true);
22108             }
22109         }
22110     },
22111
22112     /**
22113      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22114      * @param {Number} startRow The index of the first row in the range
22115      * @param {Number} endRow The index of the last row in the range
22116      */
22117     deselectRange : function(startRow, endRow, preventViewNotify){
22118         if(this.locked) {
22119             return;
22120         }
22121         for(var i = startRow; i <= endRow; i++){
22122             this.deselectRow(i, preventViewNotify);
22123         }
22124     },
22125
22126     /**
22127      * Selects a row.
22128      * @param {Number} row The index of the row to select
22129      * @param {Boolean} keepExisting (optional) True to keep existing selections
22130      */
22131     selectRow : function(index, keepExisting, preventViewNotify){
22132         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22133             return;
22134         }
22135         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22136             if(!keepExisting || this.singleSelect){
22137                 this.clearSelections();
22138             }
22139             var r = this.grid.dataSource.getAt(index);
22140             this.selections.add(r);
22141             this.last = this.lastActive = index;
22142             if(!preventViewNotify){
22143                 this.grid.getView().onRowSelect(index);
22144             }
22145             this.fireEvent("rowselect", this, index, r);
22146             this.fireEvent("selectionchange", this);
22147         }
22148     },
22149
22150     /**
22151      * Deselects a row.
22152      * @param {Number} row The index of the row to deselect
22153      */
22154     deselectRow : function(index, preventViewNotify){
22155         if(this.locked) {
22156             return;
22157         }
22158         if(this.last == index){
22159             this.last = false;
22160         }
22161         if(this.lastActive == index){
22162             this.lastActive = false;
22163         }
22164         var r = this.grid.dataSource.getAt(index);
22165         this.selections.remove(r);
22166         if(!preventViewNotify){
22167             this.grid.getView().onRowDeselect(index);
22168         }
22169         this.fireEvent("rowdeselect", this, index);
22170         this.fireEvent("selectionchange", this);
22171     },
22172
22173     // private
22174     restoreLast : function(){
22175         if(this._last){
22176             this.last = this._last;
22177         }
22178     },
22179
22180     // private
22181     acceptsNav : function(row, col, cm){
22182         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22183     },
22184
22185     // private
22186     onEditorKey : function(field, e){
22187         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22188         if(k == e.TAB){
22189             e.stopEvent();
22190             ed.completeEdit();
22191             if(e.shiftKey){
22192                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22193             }else{
22194                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22195             }
22196         }else if(k == e.ENTER && !e.ctrlKey){
22197             e.stopEvent();
22198             ed.completeEdit();
22199             if(e.shiftKey){
22200                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22201             }else{
22202                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22203             }
22204         }else if(k == e.ESC){
22205             ed.cancelEdit();
22206         }
22207         if(newCell){
22208             g.startEditing(newCell[0], newCell[1]);
22209         }
22210     }
22211 });/*
22212  * Based on:
22213  * Ext JS Library 1.1.1
22214  * Copyright(c) 2006-2007, Ext JS, LLC.
22215  *
22216  * Originally Released Under LGPL - original licence link has changed is not relivant.
22217  *
22218  * Fork - LGPL
22219  * <script type="text/javascript">
22220  */
22221  
22222 /**
22223  * @class Roo.bootstrap.PagingToolbar
22224  * @extends Roo.bootstrap.NavSimplebar
22225  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22226  * @constructor
22227  * Create a new PagingToolbar
22228  * @param {Object} config The config object
22229  * @param {Roo.data.Store} store
22230  */
22231 Roo.bootstrap.PagingToolbar = function(config)
22232 {
22233     // old args format still supported... - xtype is prefered..
22234         // created from xtype...
22235     
22236     this.ds = config.dataSource;
22237     
22238     if (config.store && !this.ds) {
22239         this.store= Roo.factory(config.store, Roo.data);
22240         this.ds = this.store;
22241         this.ds.xmodule = this.xmodule || false;
22242     }
22243     
22244     this.toolbarItems = [];
22245     if (config.items) {
22246         this.toolbarItems = config.items;
22247     }
22248     
22249     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22250     
22251     this.cursor = 0;
22252     
22253     if (this.ds) { 
22254         this.bind(this.ds);
22255     }
22256     
22257     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22258     
22259 };
22260
22261 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22262     /**
22263      * @cfg {Roo.data.Store} dataSource
22264      * The underlying data store providing the paged data
22265      */
22266     /**
22267      * @cfg {String/HTMLElement/Element} container
22268      * container The id or element that will contain the toolbar
22269      */
22270     /**
22271      * @cfg {Boolean} displayInfo
22272      * True to display the displayMsg (defaults to false)
22273      */
22274     /**
22275      * @cfg {Number} pageSize
22276      * The number of records to display per page (defaults to 20)
22277      */
22278     pageSize: 20,
22279     /**
22280      * @cfg {String} displayMsg
22281      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22282      */
22283     displayMsg : 'Displaying {0} - {1} of {2}',
22284     /**
22285      * @cfg {String} emptyMsg
22286      * The message to display when no records are found (defaults to "No data to display")
22287      */
22288     emptyMsg : 'No data to display',
22289     /**
22290      * Customizable piece of the default paging text (defaults to "Page")
22291      * @type String
22292      */
22293     beforePageText : "Page",
22294     /**
22295      * Customizable piece of the default paging text (defaults to "of %0")
22296      * @type String
22297      */
22298     afterPageText : "of {0}",
22299     /**
22300      * Customizable piece of the default paging text (defaults to "First Page")
22301      * @type String
22302      */
22303     firstText : "First Page",
22304     /**
22305      * Customizable piece of the default paging text (defaults to "Previous Page")
22306      * @type String
22307      */
22308     prevText : "Previous Page",
22309     /**
22310      * Customizable piece of the default paging text (defaults to "Next Page")
22311      * @type String
22312      */
22313     nextText : "Next Page",
22314     /**
22315      * Customizable piece of the default paging text (defaults to "Last Page")
22316      * @type String
22317      */
22318     lastText : "Last Page",
22319     /**
22320      * Customizable piece of the default paging text (defaults to "Refresh")
22321      * @type String
22322      */
22323     refreshText : "Refresh",
22324
22325     buttons : false,
22326     // private
22327     onRender : function(ct, position) 
22328     {
22329         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22330         this.navgroup.parentId = this.id;
22331         this.navgroup.onRender(this.el, null);
22332         // add the buttons to the navgroup
22333         
22334         if(this.displayInfo){
22335             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22336             this.displayEl = this.el.select('.x-paging-info', true).first();
22337 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22338 //            this.displayEl = navel.el.select('span',true).first();
22339         }
22340         
22341         var _this = this;
22342         
22343         if(this.buttons){
22344             Roo.each(_this.buttons, function(e){ // this might need to use render????
22345                Roo.factory(e).onRender(_this.el, null);
22346             });
22347         }
22348             
22349         Roo.each(_this.toolbarItems, function(e) {
22350             _this.navgroup.addItem(e);
22351         });
22352         
22353         
22354         this.first = this.navgroup.addItem({
22355             tooltip: this.firstText,
22356             cls: "prev",
22357             icon : 'fa fa-backward',
22358             disabled: true,
22359             preventDefault: true,
22360             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22361         });
22362         
22363         this.prev =  this.navgroup.addItem({
22364             tooltip: this.prevText,
22365             cls: "prev",
22366             icon : 'fa fa-step-backward',
22367             disabled: true,
22368             preventDefault: true,
22369             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22370         });
22371     //this.addSeparator();
22372         
22373         
22374         var field = this.navgroup.addItem( {
22375             tagtype : 'span',
22376             cls : 'x-paging-position',
22377             
22378             html : this.beforePageText  +
22379                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22380                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22381          } ); //?? escaped?
22382         
22383         this.field = field.el.select('input', true).first();
22384         this.field.on("keydown", this.onPagingKeydown, this);
22385         this.field.on("focus", function(){this.dom.select();});
22386     
22387     
22388         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22389         //this.field.setHeight(18);
22390         //this.addSeparator();
22391         this.next = this.navgroup.addItem({
22392             tooltip: this.nextText,
22393             cls: "next",
22394             html : ' <i class="fa fa-step-forward">',
22395             disabled: true,
22396             preventDefault: true,
22397             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22398         });
22399         this.last = this.navgroup.addItem({
22400             tooltip: this.lastText,
22401             icon : 'fa fa-forward',
22402             cls: "next",
22403             disabled: true,
22404             preventDefault: true,
22405             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22406         });
22407     //this.addSeparator();
22408         this.loading = this.navgroup.addItem({
22409             tooltip: this.refreshText,
22410             icon: 'fa fa-refresh',
22411             preventDefault: true,
22412             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22413         });
22414         
22415     },
22416
22417     // private
22418     updateInfo : function(){
22419         if(this.displayEl){
22420             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22421             var msg = count == 0 ?
22422                 this.emptyMsg :
22423                 String.format(
22424                     this.displayMsg,
22425                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22426                 );
22427             this.displayEl.update(msg);
22428         }
22429     },
22430
22431     // private
22432     onLoad : function(ds, r, o){
22433        this.cursor = o.params ? o.params.start : 0;
22434        var d = this.getPageData(),
22435             ap = d.activePage,
22436             ps = d.pages;
22437         
22438        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22439        this.field.dom.value = ap;
22440        this.first.setDisabled(ap == 1);
22441        this.prev.setDisabled(ap == 1);
22442        this.next.setDisabled(ap == ps);
22443        this.last.setDisabled(ap == ps);
22444        this.loading.enable();
22445        this.updateInfo();
22446     },
22447
22448     // private
22449     getPageData : function(){
22450         var total = this.ds.getTotalCount();
22451         return {
22452             total : total,
22453             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22454             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22455         };
22456     },
22457
22458     // private
22459     onLoadError : function(){
22460         this.loading.enable();
22461     },
22462
22463     // private
22464     onPagingKeydown : function(e){
22465         var k = e.getKey();
22466         var d = this.getPageData();
22467         if(k == e.RETURN){
22468             var v = this.field.dom.value, pageNum;
22469             if(!v || isNaN(pageNum = parseInt(v, 10))){
22470                 this.field.dom.value = d.activePage;
22471                 return;
22472             }
22473             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22474             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22475             e.stopEvent();
22476         }
22477         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))
22478         {
22479           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22480           this.field.dom.value = pageNum;
22481           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22482           e.stopEvent();
22483         }
22484         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22485         {
22486           var v = this.field.dom.value, pageNum; 
22487           var increment = (e.shiftKey) ? 10 : 1;
22488           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22489                 increment *= -1;
22490           }
22491           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22492             this.field.dom.value = d.activePage;
22493             return;
22494           }
22495           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22496           {
22497             this.field.dom.value = parseInt(v, 10) + increment;
22498             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22499             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22500           }
22501           e.stopEvent();
22502         }
22503     },
22504
22505     // private
22506     beforeLoad : function(){
22507         if(this.loading){
22508             this.loading.disable();
22509         }
22510     },
22511
22512     // private
22513     onClick : function(which){
22514         
22515         var ds = this.ds;
22516         if (!ds) {
22517             return;
22518         }
22519         
22520         switch(which){
22521             case "first":
22522                 ds.load({params:{start: 0, limit: this.pageSize}});
22523             break;
22524             case "prev":
22525                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22526             break;
22527             case "next":
22528                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22529             break;
22530             case "last":
22531                 var total = ds.getTotalCount();
22532                 var extra = total % this.pageSize;
22533                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22534                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22535             break;
22536             case "refresh":
22537                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22538             break;
22539         }
22540     },
22541
22542     /**
22543      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22544      * @param {Roo.data.Store} store The data store to unbind
22545      */
22546     unbind : function(ds){
22547         ds.un("beforeload", this.beforeLoad, this);
22548         ds.un("load", this.onLoad, this);
22549         ds.un("loadexception", this.onLoadError, this);
22550         ds.un("remove", this.updateInfo, this);
22551         ds.un("add", this.updateInfo, this);
22552         this.ds = undefined;
22553     },
22554
22555     /**
22556      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22557      * @param {Roo.data.Store} store The data store to bind
22558      */
22559     bind : function(ds){
22560         ds.on("beforeload", this.beforeLoad, this);
22561         ds.on("load", this.onLoad, this);
22562         ds.on("loadexception", this.onLoadError, this);
22563         ds.on("remove", this.updateInfo, this);
22564         ds.on("add", this.updateInfo, this);
22565         this.ds = ds;
22566     }
22567 });/*
22568  * - LGPL
22569  *
22570  * element
22571  * 
22572  */
22573
22574 /**
22575  * @class Roo.bootstrap.MessageBar
22576  * @extends Roo.bootstrap.Component
22577  * Bootstrap MessageBar class
22578  * @cfg {String} html contents of the MessageBar
22579  * @cfg {String} weight (info | success | warning | danger) default info
22580  * @cfg {String} beforeClass insert the bar before the given class
22581  * @cfg {Boolean} closable (true | false) default false
22582  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22583  * 
22584  * @constructor
22585  * Create a new Element
22586  * @param {Object} config The config object
22587  */
22588
22589 Roo.bootstrap.MessageBar = function(config){
22590     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22591 };
22592
22593 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22594     
22595     html: '',
22596     weight: 'info',
22597     closable: false,
22598     fixed: false,
22599     beforeClass: 'bootstrap-sticky-wrap',
22600     
22601     getAutoCreate : function(){
22602         
22603         var cfg = {
22604             tag: 'div',
22605             cls: 'alert alert-dismissable alert-' + this.weight,
22606             cn: [
22607                 {
22608                     tag: 'span',
22609                     cls: 'message',
22610                     html: this.html || ''
22611                 }
22612             ]
22613         };
22614         
22615         if(this.fixed){
22616             cfg.cls += ' alert-messages-fixed';
22617         }
22618         
22619         if(this.closable){
22620             cfg.cn.push({
22621                 tag: 'button',
22622                 cls: 'close',
22623                 html: 'x'
22624             });
22625         }
22626         
22627         return cfg;
22628     },
22629     
22630     onRender : function(ct, position)
22631     {
22632         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22633         
22634         if(!this.el){
22635             var cfg = Roo.apply({},  this.getAutoCreate());
22636             cfg.id = Roo.id();
22637             
22638             if (this.cls) {
22639                 cfg.cls += ' ' + this.cls;
22640             }
22641             if (this.style) {
22642                 cfg.style = this.style;
22643             }
22644             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22645             
22646             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22647         }
22648         
22649         this.el.select('>button.close').on('click', this.hide, this);
22650         
22651     },
22652     
22653     show : function()
22654     {
22655         if (!this.rendered) {
22656             this.render();
22657         }
22658         
22659         this.el.show();
22660         
22661         this.fireEvent('show', this);
22662         
22663     },
22664     
22665     hide : function()
22666     {
22667         if (!this.rendered) {
22668             this.render();
22669         }
22670         
22671         this.el.hide();
22672         
22673         this.fireEvent('hide', this);
22674     },
22675     
22676     update : function()
22677     {
22678 //        var e = this.el.dom.firstChild;
22679 //        
22680 //        if(this.closable){
22681 //            e = e.nextSibling;
22682 //        }
22683 //        
22684 //        e.data = this.html || '';
22685
22686         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22687     }
22688    
22689 });
22690
22691  
22692
22693      /*
22694  * - LGPL
22695  *
22696  * Graph
22697  * 
22698  */
22699
22700
22701 /**
22702  * @class Roo.bootstrap.Graph
22703  * @extends Roo.bootstrap.Component
22704  * Bootstrap Graph class
22705 > Prameters
22706  -sm {number} sm 4
22707  -md {number} md 5
22708  @cfg {String} graphtype  bar | vbar | pie
22709  @cfg {number} g_x coodinator | centre x (pie)
22710  @cfg {number} g_y coodinator | centre y (pie)
22711  @cfg {number} g_r radius (pie)
22712  @cfg {number} g_height height of the chart (respected by all elements in the set)
22713  @cfg {number} g_width width of the chart (respected by all elements in the set)
22714  @cfg {Object} title The title of the chart
22715     
22716  -{Array}  values
22717  -opts (object) options for the chart 
22718      o {
22719      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22720      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22721      o vgutter (number)
22722      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.
22723      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22724      o to
22725      o stretch (boolean)
22726      o }
22727  -opts (object) options for the pie
22728      o{
22729      o cut
22730      o startAngle (number)
22731      o endAngle (number)
22732      } 
22733  *
22734  * @constructor
22735  * Create a new Input
22736  * @param {Object} config The config object
22737  */
22738
22739 Roo.bootstrap.Graph = function(config){
22740     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22741     
22742     this.addEvents({
22743         // img events
22744         /**
22745          * @event click
22746          * The img click event for the img.
22747          * @param {Roo.EventObject} e
22748          */
22749         "click" : true
22750     });
22751 };
22752
22753 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22754     
22755     sm: 4,
22756     md: 5,
22757     graphtype: 'bar',
22758     g_height: 250,
22759     g_width: 400,
22760     g_x: 50,
22761     g_y: 50,
22762     g_r: 30,
22763     opts:{
22764         //g_colors: this.colors,
22765         g_type: 'soft',
22766         g_gutter: '20%'
22767
22768     },
22769     title : false,
22770
22771     getAutoCreate : function(){
22772         
22773         var cfg = {
22774             tag: 'div',
22775             html : null
22776         };
22777         
22778         
22779         return  cfg;
22780     },
22781
22782     onRender : function(ct,position){
22783         
22784         
22785         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22786         
22787         if (typeof(Raphael) == 'undefined') {
22788             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
22789             return;
22790         }
22791         
22792         this.raphael = Raphael(this.el.dom);
22793         
22794                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22795                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22796                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22797                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22798                 /*
22799                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22800                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22801                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22802                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22803                 
22804                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22805                 r.barchart(330, 10, 300, 220, data1);
22806                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22807                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22808                 */
22809                 
22810                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22811                 // r.barchart(30, 30, 560, 250,  xdata, {
22812                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22813                 //     axis : "0 0 1 1",
22814                 //     axisxlabels :  xdata
22815                 //     //yvalues : cols,
22816                    
22817                 // });
22818 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22819 //        
22820 //        this.load(null,xdata,{
22821 //                axis : "0 0 1 1",
22822 //                axisxlabels :  xdata
22823 //                });
22824
22825     },
22826
22827     load : function(graphtype,xdata,opts)
22828     {
22829         this.raphael.clear();
22830         if(!graphtype) {
22831             graphtype = this.graphtype;
22832         }
22833         if(!opts){
22834             opts = this.opts;
22835         }
22836         var r = this.raphael,
22837             fin = function () {
22838                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22839             },
22840             fout = function () {
22841                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22842             },
22843             pfin = function() {
22844                 this.sector.stop();
22845                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22846
22847                 if (this.label) {
22848                     this.label[0].stop();
22849                     this.label[0].attr({ r: 7.5 });
22850                     this.label[1].attr({ "font-weight": 800 });
22851                 }
22852             },
22853             pfout = function() {
22854                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22855
22856                 if (this.label) {
22857                     this.label[0].animate({ r: 5 }, 500, "bounce");
22858                     this.label[1].attr({ "font-weight": 400 });
22859                 }
22860             };
22861
22862         switch(graphtype){
22863             case 'bar':
22864                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22865                 break;
22866             case 'hbar':
22867                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22868                 break;
22869             case 'pie':
22870 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22871 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22872 //            
22873                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22874                 
22875                 break;
22876
22877         }
22878         
22879         if(this.title){
22880             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22881         }
22882         
22883     },
22884     
22885     setTitle: function(o)
22886     {
22887         this.title = o;
22888     },
22889     
22890     initEvents: function() {
22891         
22892         if(!this.href){
22893             this.el.on('click', this.onClick, this);
22894         }
22895     },
22896     
22897     onClick : function(e)
22898     {
22899         Roo.log('img onclick');
22900         this.fireEvent('click', this, e);
22901     }
22902    
22903 });
22904
22905  
22906 /*
22907  * - LGPL
22908  *
22909  * numberBox
22910  * 
22911  */
22912 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22913
22914 /**
22915  * @class Roo.bootstrap.dash.NumberBox
22916  * @extends Roo.bootstrap.Component
22917  * Bootstrap NumberBox class
22918  * @cfg {String} headline Box headline
22919  * @cfg {String} content Box content
22920  * @cfg {String} icon Box icon
22921  * @cfg {String} footer Footer text
22922  * @cfg {String} fhref Footer href
22923  * 
22924  * @constructor
22925  * Create a new NumberBox
22926  * @param {Object} config The config object
22927  */
22928
22929
22930 Roo.bootstrap.dash.NumberBox = function(config){
22931     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22932     
22933 };
22934
22935 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22936     
22937     headline : '',
22938     content : '',
22939     icon : '',
22940     footer : '',
22941     fhref : '',
22942     ficon : '',
22943     
22944     getAutoCreate : function(){
22945         
22946         var cfg = {
22947             tag : 'div',
22948             cls : 'small-box ',
22949             cn : [
22950                 {
22951                     tag : 'div',
22952                     cls : 'inner',
22953                     cn :[
22954                         {
22955                             tag : 'h3',
22956                             cls : 'roo-headline',
22957                             html : this.headline
22958                         },
22959                         {
22960                             tag : 'p',
22961                             cls : 'roo-content',
22962                             html : this.content
22963                         }
22964                     ]
22965                 }
22966             ]
22967         };
22968         
22969         if(this.icon){
22970             cfg.cn.push({
22971                 tag : 'div',
22972                 cls : 'icon',
22973                 cn :[
22974                     {
22975                         tag : 'i',
22976                         cls : 'ion ' + this.icon
22977                     }
22978                 ]
22979             });
22980         }
22981         
22982         if(this.footer){
22983             var footer = {
22984                 tag : 'a',
22985                 cls : 'small-box-footer',
22986                 href : this.fhref || '#',
22987                 html : this.footer
22988             };
22989             
22990             cfg.cn.push(footer);
22991             
22992         }
22993         
22994         return  cfg;
22995     },
22996
22997     onRender : function(ct,position){
22998         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22999
23000
23001        
23002                 
23003     },
23004
23005     setHeadline: function (value)
23006     {
23007         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23008     },
23009     
23010     setFooter: function (value, href)
23011     {
23012         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23013         
23014         if(href){
23015             this.el.select('a.small-box-footer',true).first().attr('href', href);
23016         }
23017         
23018     },
23019
23020     setContent: function (value)
23021     {
23022         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23023     },
23024
23025     initEvents: function() 
23026     {   
23027         
23028     }
23029     
23030 });
23031
23032  
23033 /*
23034  * - LGPL
23035  *
23036  * TabBox
23037  * 
23038  */
23039 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23040
23041 /**
23042  * @class Roo.bootstrap.dash.TabBox
23043  * @extends Roo.bootstrap.Component
23044  * Bootstrap TabBox class
23045  * @cfg {String} title Title of the TabBox
23046  * @cfg {String} icon Icon of the TabBox
23047  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23048  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23049  * 
23050  * @constructor
23051  * Create a new TabBox
23052  * @param {Object} config The config object
23053  */
23054
23055
23056 Roo.bootstrap.dash.TabBox = function(config){
23057     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23058     this.addEvents({
23059         // raw events
23060         /**
23061          * @event addpane
23062          * When a pane is added
23063          * @param {Roo.bootstrap.dash.TabPane} pane
23064          */
23065         "addpane" : true,
23066         /**
23067          * @event activatepane
23068          * When a pane is activated
23069          * @param {Roo.bootstrap.dash.TabPane} pane
23070          */
23071         "activatepane" : true
23072         
23073          
23074     });
23075     
23076     this.panes = [];
23077 };
23078
23079 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23080
23081     title : '',
23082     icon : false,
23083     showtabs : true,
23084     tabScrollable : false,
23085     
23086     getChildContainer : function()
23087     {
23088         return this.el.select('.tab-content', true).first();
23089     },
23090     
23091     getAutoCreate : function(){
23092         
23093         var header = {
23094             tag: 'li',
23095             cls: 'pull-left header',
23096             html: this.title,
23097             cn : []
23098         };
23099         
23100         if(this.icon){
23101             header.cn.push({
23102                 tag: 'i',
23103                 cls: 'fa ' + this.icon
23104             });
23105         }
23106         
23107         var h = {
23108             tag: 'ul',
23109             cls: 'nav nav-tabs pull-right',
23110             cn: [
23111                 header
23112             ]
23113         };
23114         
23115         if(this.tabScrollable){
23116             h = {
23117                 tag: 'div',
23118                 cls: 'tab-header',
23119                 cn: [
23120                     {
23121                         tag: 'ul',
23122                         cls: 'nav nav-tabs pull-right',
23123                         cn: [
23124                             header
23125                         ]
23126                     }
23127                 ]
23128             };
23129         }
23130         
23131         var cfg = {
23132             tag: 'div',
23133             cls: 'nav-tabs-custom',
23134             cn: [
23135                 h,
23136                 {
23137                     tag: 'div',
23138                     cls: 'tab-content no-padding',
23139                     cn: []
23140                 }
23141             ]
23142         };
23143
23144         return  cfg;
23145     },
23146     initEvents : function()
23147     {
23148         //Roo.log('add add pane handler');
23149         this.on('addpane', this.onAddPane, this);
23150     },
23151      /**
23152      * Updates the box title
23153      * @param {String} html to set the title to.
23154      */
23155     setTitle : function(value)
23156     {
23157         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23158     },
23159     onAddPane : function(pane)
23160     {
23161         this.panes.push(pane);
23162         //Roo.log('addpane');
23163         //Roo.log(pane);
23164         // tabs are rendere left to right..
23165         if(!this.showtabs){
23166             return;
23167         }
23168         
23169         var ctr = this.el.select('.nav-tabs', true).first();
23170          
23171          
23172         var existing = ctr.select('.nav-tab',true);
23173         var qty = existing.getCount();;
23174         
23175         
23176         var tab = ctr.createChild({
23177             tag : 'li',
23178             cls : 'nav-tab' + (qty ? '' : ' active'),
23179             cn : [
23180                 {
23181                     tag : 'a',
23182                     href:'#',
23183                     html : pane.title
23184                 }
23185             ]
23186         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23187         pane.tab = tab;
23188         
23189         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23190         if (!qty) {
23191             pane.el.addClass('active');
23192         }
23193         
23194                 
23195     },
23196     onTabClick : function(ev,un,ob,pane)
23197     {
23198         //Roo.log('tab - prev default');
23199         ev.preventDefault();
23200         
23201         
23202         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23203         pane.tab.addClass('active');
23204         //Roo.log(pane.title);
23205         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23206         // technically we should have a deactivate event.. but maybe add later.
23207         // and it should not de-activate the selected tab...
23208         this.fireEvent('activatepane', pane);
23209         pane.el.addClass('active');
23210         pane.fireEvent('activate');
23211         
23212         
23213     },
23214     
23215     getActivePane : function()
23216     {
23217         var r = false;
23218         Roo.each(this.panes, function(p) {
23219             if(p.el.hasClass('active')){
23220                 r = p;
23221                 return false;
23222             }
23223             
23224             return;
23225         });
23226         
23227         return r;
23228     }
23229     
23230     
23231 });
23232
23233  
23234 /*
23235  * - LGPL
23236  *
23237  * Tab pane
23238  * 
23239  */
23240 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23241 /**
23242  * @class Roo.bootstrap.TabPane
23243  * @extends Roo.bootstrap.Component
23244  * Bootstrap TabPane class
23245  * @cfg {Boolean} active (false | true) Default false
23246  * @cfg {String} title title of panel
23247
23248  * 
23249  * @constructor
23250  * Create a new TabPane
23251  * @param {Object} config The config object
23252  */
23253
23254 Roo.bootstrap.dash.TabPane = function(config){
23255     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23256     
23257     this.addEvents({
23258         // raw events
23259         /**
23260          * @event activate
23261          * When a pane is activated
23262          * @param {Roo.bootstrap.dash.TabPane} pane
23263          */
23264         "activate" : true
23265          
23266     });
23267 };
23268
23269 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23270     
23271     active : false,
23272     title : '',
23273     
23274     // the tabBox that this is attached to.
23275     tab : false,
23276      
23277     getAutoCreate : function() 
23278     {
23279         var cfg = {
23280             tag: 'div',
23281             cls: 'tab-pane'
23282         };
23283         
23284         if(this.active){
23285             cfg.cls += ' active';
23286         }
23287         
23288         return cfg;
23289     },
23290     initEvents  : function()
23291     {
23292         //Roo.log('trigger add pane handler');
23293         this.parent().fireEvent('addpane', this)
23294     },
23295     
23296      /**
23297      * Updates the tab title 
23298      * @param {String} html to set the title to.
23299      */
23300     setTitle: function(str)
23301     {
23302         if (!this.tab) {
23303             return;
23304         }
23305         this.title = str;
23306         this.tab.select('a', true).first().dom.innerHTML = str;
23307         
23308     }
23309     
23310     
23311     
23312 });
23313
23314  
23315
23316
23317  /*
23318  * - LGPL
23319  *
23320  * menu
23321  * 
23322  */
23323 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23324
23325 /**
23326  * @class Roo.bootstrap.menu.Menu
23327  * @extends Roo.bootstrap.Component
23328  * Bootstrap Menu class - container for Menu
23329  * @cfg {String} html Text of the menu
23330  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23331  * @cfg {String} icon Font awesome icon
23332  * @cfg {String} pos Menu align to (top | bottom) default bottom
23333  * 
23334  * 
23335  * @constructor
23336  * Create a new Menu
23337  * @param {Object} config The config object
23338  */
23339
23340
23341 Roo.bootstrap.menu.Menu = function(config){
23342     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23343     
23344     this.addEvents({
23345         /**
23346          * @event beforeshow
23347          * Fires before this menu is displayed
23348          * @param {Roo.bootstrap.menu.Menu} this
23349          */
23350         beforeshow : true,
23351         /**
23352          * @event beforehide
23353          * Fires before this menu is hidden
23354          * @param {Roo.bootstrap.menu.Menu} this
23355          */
23356         beforehide : true,
23357         /**
23358          * @event show
23359          * Fires after this menu is displayed
23360          * @param {Roo.bootstrap.menu.Menu} this
23361          */
23362         show : true,
23363         /**
23364          * @event hide
23365          * Fires after this menu is hidden
23366          * @param {Roo.bootstrap.menu.Menu} this
23367          */
23368         hide : true,
23369         /**
23370          * @event click
23371          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23372          * @param {Roo.bootstrap.menu.Menu} this
23373          * @param {Roo.EventObject} e
23374          */
23375         click : true
23376     });
23377     
23378 };
23379
23380 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23381     
23382     submenu : false,
23383     html : '',
23384     weight : 'default',
23385     icon : false,
23386     pos : 'bottom',
23387     
23388     
23389     getChildContainer : function() {
23390         if(this.isSubMenu){
23391             return this.el;
23392         }
23393         
23394         return this.el.select('ul.dropdown-menu', true).first();  
23395     },
23396     
23397     getAutoCreate : function()
23398     {
23399         var text = [
23400             {
23401                 tag : 'span',
23402                 cls : 'roo-menu-text',
23403                 html : this.html
23404             }
23405         ];
23406         
23407         if(this.icon){
23408             text.unshift({
23409                 tag : 'i',
23410                 cls : 'fa ' + this.icon
23411             })
23412         }
23413         
23414         
23415         var cfg = {
23416             tag : 'div',
23417             cls : 'btn-group',
23418             cn : [
23419                 {
23420                     tag : 'button',
23421                     cls : 'dropdown-button btn btn-' + this.weight,
23422                     cn : text
23423                 },
23424                 {
23425                     tag : 'button',
23426                     cls : 'dropdown-toggle btn btn-' + this.weight,
23427                     cn : [
23428                         {
23429                             tag : 'span',
23430                             cls : 'caret'
23431                         }
23432                     ]
23433                 },
23434                 {
23435                     tag : 'ul',
23436                     cls : 'dropdown-menu'
23437                 }
23438             ]
23439             
23440         };
23441         
23442         if(this.pos == 'top'){
23443             cfg.cls += ' dropup';
23444         }
23445         
23446         if(this.isSubMenu){
23447             cfg = {
23448                 tag : 'ul',
23449                 cls : 'dropdown-menu'
23450             }
23451         }
23452         
23453         return cfg;
23454     },
23455     
23456     onRender : function(ct, position)
23457     {
23458         this.isSubMenu = ct.hasClass('dropdown-submenu');
23459         
23460         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23461     },
23462     
23463     initEvents : function() 
23464     {
23465         if(this.isSubMenu){
23466             return;
23467         }
23468         
23469         this.hidden = true;
23470         
23471         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23472         this.triggerEl.on('click', this.onTriggerPress, this);
23473         
23474         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23475         this.buttonEl.on('click', this.onClick, this);
23476         
23477     },
23478     
23479     list : function()
23480     {
23481         if(this.isSubMenu){
23482             return this.el;
23483         }
23484         
23485         return this.el.select('ul.dropdown-menu', true).first();
23486     },
23487     
23488     onClick : function(e)
23489     {
23490         this.fireEvent("click", this, e);
23491     },
23492     
23493     onTriggerPress  : function(e)
23494     {   
23495         if (this.isVisible()) {
23496             this.hide();
23497         } else {
23498             this.show();
23499         }
23500     },
23501     
23502     isVisible : function(){
23503         return !this.hidden;
23504     },
23505     
23506     show : function()
23507     {
23508         this.fireEvent("beforeshow", this);
23509         
23510         this.hidden = false;
23511         this.el.addClass('open');
23512         
23513         Roo.get(document).on("mouseup", this.onMouseUp, this);
23514         
23515         this.fireEvent("show", this);
23516         
23517         
23518     },
23519     
23520     hide : function()
23521     {
23522         this.fireEvent("beforehide", this);
23523         
23524         this.hidden = true;
23525         this.el.removeClass('open');
23526         
23527         Roo.get(document).un("mouseup", this.onMouseUp);
23528         
23529         this.fireEvent("hide", this);
23530     },
23531     
23532     onMouseUp : function()
23533     {
23534         this.hide();
23535     }
23536     
23537 });
23538
23539  
23540  /*
23541  * - LGPL
23542  *
23543  * menu item
23544  * 
23545  */
23546 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23547
23548 /**
23549  * @class Roo.bootstrap.menu.Item
23550  * @extends Roo.bootstrap.Component
23551  * Bootstrap MenuItem class
23552  * @cfg {Boolean} submenu (true | false) default false
23553  * @cfg {String} html text of the item
23554  * @cfg {String} href the link
23555  * @cfg {Boolean} disable (true | false) default false
23556  * @cfg {Boolean} preventDefault (true | false) default true
23557  * @cfg {String} icon Font awesome icon
23558  * @cfg {String} pos Submenu align to (left | right) default right 
23559  * 
23560  * 
23561  * @constructor
23562  * Create a new Item
23563  * @param {Object} config The config object
23564  */
23565
23566
23567 Roo.bootstrap.menu.Item = function(config){
23568     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23569     this.addEvents({
23570         /**
23571          * @event mouseover
23572          * Fires when the mouse is hovering over this menu
23573          * @param {Roo.bootstrap.menu.Item} this
23574          * @param {Roo.EventObject} e
23575          */
23576         mouseover : true,
23577         /**
23578          * @event mouseout
23579          * Fires when the mouse exits this menu
23580          * @param {Roo.bootstrap.menu.Item} this
23581          * @param {Roo.EventObject} e
23582          */
23583         mouseout : true,
23584         // raw events
23585         /**
23586          * @event click
23587          * The raw click event for the entire grid.
23588          * @param {Roo.EventObject} e
23589          */
23590         click : true
23591     });
23592 };
23593
23594 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23595     
23596     submenu : false,
23597     href : '',
23598     html : '',
23599     preventDefault: true,
23600     disable : false,
23601     icon : false,
23602     pos : 'right',
23603     
23604     getAutoCreate : function()
23605     {
23606         var text = [
23607             {
23608                 tag : 'span',
23609                 cls : 'roo-menu-item-text',
23610                 html : this.html
23611             }
23612         ];
23613         
23614         if(this.icon){
23615             text.unshift({
23616                 tag : 'i',
23617                 cls : 'fa ' + this.icon
23618             })
23619         }
23620         
23621         var cfg = {
23622             tag : 'li',
23623             cn : [
23624                 {
23625                     tag : 'a',
23626                     href : this.href || '#',
23627                     cn : text
23628                 }
23629             ]
23630         };
23631         
23632         if(this.disable){
23633             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23634         }
23635         
23636         if(this.submenu){
23637             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23638             
23639             if(this.pos == 'left'){
23640                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23641             }
23642         }
23643         
23644         return cfg;
23645     },
23646     
23647     initEvents : function() 
23648     {
23649         this.el.on('mouseover', this.onMouseOver, this);
23650         this.el.on('mouseout', this.onMouseOut, this);
23651         
23652         this.el.select('a', true).first().on('click', this.onClick, this);
23653         
23654     },
23655     
23656     onClick : function(e)
23657     {
23658         if(this.preventDefault){
23659             e.preventDefault();
23660         }
23661         
23662         this.fireEvent("click", this, e);
23663     },
23664     
23665     onMouseOver : function(e)
23666     {
23667         if(this.submenu && this.pos == 'left'){
23668             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23669         }
23670         
23671         this.fireEvent("mouseover", this, e);
23672     },
23673     
23674     onMouseOut : function(e)
23675     {
23676         this.fireEvent("mouseout", this, e);
23677     }
23678 });
23679
23680  
23681
23682  /*
23683  * - LGPL
23684  *
23685  * menu separator
23686  * 
23687  */
23688 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23689
23690 /**
23691  * @class Roo.bootstrap.menu.Separator
23692  * @extends Roo.bootstrap.Component
23693  * Bootstrap Separator class
23694  * 
23695  * @constructor
23696  * Create a new Separator
23697  * @param {Object} config The config object
23698  */
23699
23700
23701 Roo.bootstrap.menu.Separator = function(config){
23702     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23703 };
23704
23705 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23706     
23707     getAutoCreate : function(){
23708         var cfg = {
23709             tag : 'li',
23710             cls: 'divider'
23711         };
23712         
23713         return cfg;
23714     }
23715    
23716 });
23717
23718  
23719
23720  /*
23721  * - LGPL
23722  *
23723  * Tooltip
23724  * 
23725  */
23726
23727 /**
23728  * @class Roo.bootstrap.Tooltip
23729  * Bootstrap Tooltip class
23730  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23731  * to determine which dom element triggers the tooltip.
23732  * 
23733  * It needs to add support for additional attributes like tooltip-position
23734  * 
23735  * @constructor
23736  * Create a new Toolti
23737  * @param {Object} config The config object
23738  */
23739
23740 Roo.bootstrap.Tooltip = function(config){
23741     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23742 };
23743
23744 Roo.apply(Roo.bootstrap.Tooltip, {
23745     /**
23746      * @function init initialize tooltip monitoring.
23747      * @static
23748      */
23749     currentEl : false,
23750     currentTip : false,
23751     currentRegion : false,
23752     
23753     //  init : delay?
23754     
23755     init : function()
23756     {
23757         Roo.get(document).on('mouseover', this.enter ,this);
23758         Roo.get(document).on('mouseout', this.leave, this);
23759          
23760         
23761         this.currentTip = new Roo.bootstrap.Tooltip();
23762     },
23763     
23764     enter : function(ev)
23765     {
23766         var dom = ev.getTarget();
23767         
23768         //Roo.log(['enter',dom]);
23769         var el = Roo.fly(dom);
23770         if (this.currentEl) {
23771             //Roo.log(dom);
23772             //Roo.log(this.currentEl);
23773             //Roo.log(this.currentEl.contains(dom));
23774             if (this.currentEl == el) {
23775                 return;
23776             }
23777             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23778                 return;
23779             }
23780
23781         }
23782         
23783         if (this.currentTip.el) {
23784             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23785         }    
23786         //Roo.log(ev);
23787         var bindEl = el;
23788         
23789         // you can not look for children, as if el is the body.. then everythign is the child..
23790         if (!el.attr('tooltip')) { //
23791             if (!el.select("[tooltip]").elements.length) {
23792                 return;
23793             }
23794             // is the mouse over this child...?
23795             bindEl = el.select("[tooltip]").first();
23796             var xy = ev.getXY();
23797             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23798                 //Roo.log("not in region.");
23799                 return;
23800             }
23801             //Roo.log("child element over..");
23802             
23803         }
23804         this.currentEl = bindEl;
23805         this.currentTip.bind(bindEl);
23806         this.currentRegion = Roo.lib.Region.getRegion(dom);
23807         this.currentTip.enter();
23808         
23809     },
23810     leave : function(ev)
23811     {
23812         var dom = ev.getTarget();
23813         //Roo.log(['leave',dom]);
23814         if (!this.currentEl) {
23815             return;
23816         }
23817         
23818         
23819         if (dom != this.currentEl.dom) {
23820             return;
23821         }
23822         var xy = ev.getXY();
23823         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23824             return;
23825         }
23826         // only activate leave if mouse cursor is outside... bounding box..
23827         
23828         
23829         
23830         
23831         if (this.currentTip) {
23832             this.currentTip.leave();
23833         }
23834         //Roo.log('clear currentEl');
23835         this.currentEl = false;
23836         
23837         
23838     },
23839     alignment : {
23840         'left' : ['r-l', [-2,0], 'right'],
23841         'right' : ['l-r', [2,0], 'left'],
23842         'bottom' : ['t-b', [0,2], 'top'],
23843         'top' : [ 'b-t', [0,-2], 'bottom']
23844     }
23845     
23846 });
23847
23848
23849 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23850     
23851     
23852     bindEl : false,
23853     
23854     delay : null, // can be { show : 300 , hide: 500}
23855     
23856     timeout : null,
23857     
23858     hoverState : null, //???
23859     
23860     placement : 'bottom', 
23861     
23862     getAutoCreate : function(){
23863     
23864         var cfg = {
23865            cls : 'tooltip',
23866            role : 'tooltip',
23867            cn : [
23868                 {
23869                     cls : 'tooltip-arrow'
23870                 },
23871                 {
23872                     cls : 'tooltip-inner'
23873                 }
23874            ]
23875         };
23876         
23877         return cfg;
23878     },
23879     bind : function(el)
23880     {
23881         this.bindEl = el;
23882     },
23883       
23884     
23885     enter : function () {
23886        
23887         if (this.timeout != null) {
23888             clearTimeout(this.timeout);
23889         }
23890         
23891         this.hoverState = 'in';
23892          //Roo.log("enter - show");
23893         if (!this.delay || !this.delay.show) {
23894             this.show();
23895             return;
23896         }
23897         var _t = this;
23898         this.timeout = setTimeout(function () {
23899             if (_t.hoverState == 'in') {
23900                 _t.show();
23901             }
23902         }, this.delay.show);
23903     },
23904     leave : function()
23905     {
23906         clearTimeout(this.timeout);
23907     
23908         this.hoverState = 'out';
23909          if (!this.delay || !this.delay.hide) {
23910             this.hide();
23911             return;
23912         }
23913        
23914         var _t = this;
23915         this.timeout = setTimeout(function () {
23916             //Roo.log("leave - timeout");
23917             
23918             if (_t.hoverState == 'out') {
23919                 _t.hide();
23920                 Roo.bootstrap.Tooltip.currentEl = false;
23921             }
23922         }, delay);
23923     },
23924     
23925     show : function ()
23926     {
23927         if (!this.el) {
23928             this.render(document.body);
23929         }
23930         // set content.
23931         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23932         
23933         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23934         
23935         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23936         
23937         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23938         
23939         var placement = typeof this.placement == 'function' ?
23940             this.placement.call(this, this.el, on_el) :
23941             this.placement;
23942             
23943         var autoToken = /\s?auto?\s?/i;
23944         var autoPlace = autoToken.test(placement);
23945         if (autoPlace) {
23946             placement = placement.replace(autoToken, '') || 'top';
23947         }
23948         
23949         //this.el.detach()
23950         //this.el.setXY([0,0]);
23951         this.el.show();
23952         //this.el.dom.style.display='block';
23953         
23954         //this.el.appendTo(on_el);
23955         
23956         var p = this.getPosition();
23957         var box = this.el.getBox();
23958         
23959         if (autoPlace) {
23960             // fixme..
23961         }
23962         
23963         var align = Roo.bootstrap.Tooltip.alignment[placement];
23964         
23965         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23966         
23967         if(placement == 'top' || placement == 'bottom'){
23968             if(xy[0] < 0){
23969                 placement = 'right';
23970             }
23971             
23972             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23973                 placement = 'left';
23974             }
23975         }
23976         
23977         align = Roo.bootstrap.Tooltip.alignment[placement];
23978         
23979         this.el.alignTo(this.bindEl, align[0],align[1]);
23980         //var arrow = this.el.select('.arrow',true).first();
23981         //arrow.set(align[2], 
23982         
23983         this.el.addClass(placement);
23984         
23985         this.el.addClass('in fade');
23986         
23987         this.hoverState = null;
23988         
23989         if (this.el.hasClass('fade')) {
23990             // fade it?
23991         }
23992         
23993     },
23994     hide : function()
23995     {
23996          
23997         if (!this.el) {
23998             return;
23999         }
24000         //this.el.setXY([0,0]);
24001         this.el.removeClass('in');
24002         //this.el.hide();
24003         
24004     }
24005     
24006 });
24007  
24008
24009  /*
24010  * - LGPL
24011  *
24012  * Location Picker
24013  * 
24014  */
24015
24016 /**
24017  * @class Roo.bootstrap.LocationPicker
24018  * @extends Roo.bootstrap.Component
24019  * Bootstrap LocationPicker class
24020  * @cfg {Number} latitude Position when init default 0
24021  * @cfg {Number} longitude Position when init default 0
24022  * @cfg {Number} zoom default 15
24023  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24024  * @cfg {Boolean} mapTypeControl default false
24025  * @cfg {Boolean} disableDoubleClickZoom default false
24026  * @cfg {Boolean} scrollwheel default true
24027  * @cfg {Boolean} streetViewControl default false
24028  * @cfg {Number} radius default 0
24029  * @cfg {String} locationName
24030  * @cfg {Boolean} draggable default true
24031  * @cfg {Boolean} enableAutocomplete default false
24032  * @cfg {Boolean} enableReverseGeocode default true
24033  * @cfg {String} markerTitle
24034  * 
24035  * @constructor
24036  * Create a new LocationPicker
24037  * @param {Object} config The config object
24038  */
24039
24040
24041 Roo.bootstrap.LocationPicker = function(config){
24042     
24043     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24044     
24045     this.addEvents({
24046         /**
24047          * @event initial
24048          * Fires when the picker initialized.
24049          * @param {Roo.bootstrap.LocationPicker} this
24050          * @param {Google Location} location
24051          */
24052         initial : true,
24053         /**
24054          * @event positionchanged
24055          * Fires when the picker position changed.
24056          * @param {Roo.bootstrap.LocationPicker} this
24057          * @param {Google Location} location
24058          */
24059         positionchanged : true,
24060         /**
24061          * @event resize
24062          * Fires when the map resize.
24063          * @param {Roo.bootstrap.LocationPicker} this
24064          */
24065         resize : true,
24066         /**
24067          * @event show
24068          * Fires when the map show.
24069          * @param {Roo.bootstrap.LocationPicker} this
24070          */
24071         show : true,
24072         /**
24073          * @event hide
24074          * Fires when the map hide.
24075          * @param {Roo.bootstrap.LocationPicker} this
24076          */
24077         hide : true,
24078         /**
24079          * @event mapClick
24080          * Fires when click the map.
24081          * @param {Roo.bootstrap.LocationPicker} this
24082          * @param {Map event} e
24083          */
24084         mapClick : true,
24085         /**
24086          * @event mapRightClick
24087          * Fires when right click the map.
24088          * @param {Roo.bootstrap.LocationPicker} this
24089          * @param {Map event} e
24090          */
24091         mapRightClick : true,
24092         /**
24093          * @event markerClick
24094          * Fires when click the marker.
24095          * @param {Roo.bootstrap.LocationPicker} this
24096          * @param {Map event} e
24097          */
24098         markerClick : true,
24099         /**
24100          * @event markerRightClick
24101          * Fires when right click the marker.
24102          * @param {Roo.bootstrap.LocationPicker} this
24103          * @param {Map event} e
24104          */
24105         markerRightClick : true,
24106         /**
24107          * @event OverlayViewDraw
24108          * Fires when OverlayView Draw
24109          * @param {Roo.bootstrap.LocationPicker} this
24110          */
24111         OverlayViewDraw : true,
24112         /**
24113          * @event OverlayViewOnAdd
24114          * Fires when OverlayView Draw
24115          * @param {Roo.bootstrap.LocationPicker} this
24116          */
24117         OverlayViewOnAdd : true,
24118         /**
24119          * @event OverlayViewOnRemove
24120          * Fires when OverlayView Draw
24121          * @param {Roo.bootstrap.LocationPicker} this
24122          */
24123         OverlayViewOnRemove : true,
24124         /**
24125          * @event OverlayViewShow
24126          * Fires when OverlayView Draw
24127          * @param {Roo.bootstrap.LocationPicker} this
24128          * @param {Pixel} cpx
24129          */
24130         OverlayViewShow : true,
24131         /**
24132          * @event OverlayViewHide
24133          * Fires when OverlayView Draw
24134          * @param {Roo.bootstrap.LocationPicker} this
24135          */
24136         OverlayViewHide : true,
24137         /**
24138          * @event loadexception
24139          * Fires when load google lib failed.
24140          * @param {Roo.bootstrap.LocationPicker} this
24141          */
24142         loadexception : true
24143     });
24144         
24145 };
24146
24147 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24148     
24149     gMapContext: false,
24150     
24151     latitude: 0,
24152     longitude: 0,
24153     zoom: 15,
24154     mapTypeId: false,
24155     mapTypeControl: false,
24156     disableDoubleClickZoom: false,
24157     scrollwheel: true,
24158     streetViewControl: false,
24159     radius: 0,
24160     locationName: '',
24161     draggable: true,
24162     enableAutocomplete: false,
24163     enableReverseGeocode: true,
24164     markerTitle: '',
24165     
24166     getAutoCreate: function()
24167     {
24168
24169         var cfg = {
24170             tag: 'div',
24171             cls: 'roo-location-picker'
24172         };
24173         
24174         return cfg
24175     },
24176     
24177     initEvents: function(ct, position)
24178     {       
24179         if(!this.el.getWidth() || this.isApplied()){
24180             return;
24181         }
24182         
24183         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24184         
24185         this.initial();
24186     },
24187     
24188     initial: function()
24189     {
24190         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24191             this.fireEvent('loadexception', this);
24192             return;
24193         }
24194         
24195         if(!this.mapTypeId){
24196             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24197         }
24198         
24199         this.gMapContext = this.GMapContext();
24200         
24201         this.initOverlayView();
24202         
24203         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24204         
24205         var _this = this;
24206                 
24207         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24208             _this.setPosition(_this.gMapContext.marker.position);
24209         });
24210         
24211         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24212             _this.fireEvent('mapClick', this, event);
24213             
24214         });
24215
24216         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24217             _this.fireEvent('mapRightClick', this, event);
24218             
24219         });
24220         
24221         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24222             _this.fireEvent('markerClick', this, event);
24223             
24224         });
24225
24226         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24227             _this.fireEvent('markerRightClick', this, event);
24228             
24229         });
24230         
24231         this.setPosition(this.gMapContext.location);
24232         
24233         this.fireEvent('initial', this, this.gMapContext.location);
24234     },
24235     
24236     initOverlayView: function()
24237     {
24238         var _this = this;
24239         
24240         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24241             
24242             draw: function()
24243             {
24244                 _this.fireEvent('OverlayViewDraw', _this);
24245             },
24246             
24247             onAdd: function()
24248             {
24249                 _this.fireEvent('OverlayViewOnAdd', _this);
24250             },
24251             
24252             onRemove: function()
24253             {
24254                 _this.fireEvent('OverlayViewOnRemove', _this);
24255             },
24256             
24257             show: function(cpx)
24258             {
24259                 _this.fireEvent('OverlayViewShow', _this, cpx);
24260             },
24261             
24262             hide: function()
24263             {
24264                 _this.fireEvent('OverlayViewHide', _this);
24265             }
24266             
24267         });
24268     },
24269     
24270     fromLatLngToContainerPixel: function(event)
24271     {
24272         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24273     },
24274     
24275     isApplied: function() 
24276     {
24277         return this.getGmapContext() == false ? false : true;
24278     },
24279     
24280     getGmapContext: function() 
24281     {
24282         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24283     },
24284     
24285     GMapContext: function() 
24286     {
24287         var position = new google.maps.LatLng(this.latitude, this.longitude);
24288         
24289         var _map = new google.maps.Map(this.el.dom, {
24290             center: position,
24291             zoom: this.zoom,
24292             mapTypeId: this.mapTypeId,
24293             mapTypeControl: this.mapTypeControl,
24294             disableDoubleClickZoom: this.disableDoubleClickZoom,
24295             scrollwheel: this.scrollwheel,
24296             streetViewControl: this.streetViewControl,
24297             locationName: this.locationName,
24298             draggable: this.draggable,
24299             enableAutocomplete: this.enableAutocomplete,
24300             enableReverseGeocode: this.enableReverseGeocode
24301         });
24302         
24303         var _marker = new google.maps.Marker({
24304             position: position,
24305             map: _map,
24306             title: this.markerTitle,
24307             draggable: this.draggable
24308         });
24309         
24310         return {
24311             map: _map,
24312             marker: _marker,
24313             circle: null,
24314             location: position,
24315             radius: this.radius,
24316             locationName: this.locationName,
24317             addressComponents: {
24318                 formatted_address: null,
24319                 addressLine1: null,
24320                 addressLine2: null,
24321                 streetName: null,
24322                 streetNumber: null,
24323                 city: null,
24324                 district: null,
24325                 state: null,
24326                 stateOrProvince: null
24327             },
24328             settings: this,
24329             domContainer: this.el.dom,
24330             geodecoder: new google.maps.Geocoder()
24331         };
24332     },
24333     
24334     drawCircle: function(center, radius, options) 
24335     {
24336         if (this.gMapContext.circle != null) {
24337             this.gMapContext.circle.setMap(null);
24338         }
24339         if (radius > 0) {
24340             radius *= 1;
24341             options = Roo.apply({}, options, {
24342                 strokeColor: "#0000FF",
24343                 strokeOpacity: .35,
24344                 strokeWeight: 2,
24345                 fillColor: "#0000FF",
24346                 fillOpacity: .2
24347             });
24348             
24349             options.map = this.gMapContext.map;
24350             options.radius = radius;
24351             options.center = center;
24352             this.gMapContext.circle = new google.maps.Circle(options);
24353             return this.gMapContext.circle;
24354         }
24355         
24356         return null;
24357     },
24358     
24359     setPosition: function(location) 
24360     {
24361         this.gMapContext.location = location;
24362         this.gMapContext.marker.setPosition(location);
24363         this.gMapContext.map.panTo(location);
24364         this.drawCircle(location, this.gMapContext.radius, {});
24365         
24366         var _this = this;
24367         
24368         if (this.gMapContext.settings.enableReverseGeocode) {
24369             this.gMapContext.geodecoder.geocode({
24370                 latLng: this.gMapContext.location
24371             }, function(results, status) {
24372                 
24373                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24374                     _this.gMapContext.locationName = results[0].formatted_address;
24375                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24376                     
24377                     _this.fireEvent('positionchanged', this, location);
24378                 }
24379             });
24380             
24381             return;
24382         }
24383         
24384         this.fireEvent('positionchanged', this, location);
24385     },
24386     
24387     resize: function()
24388     {
24389         google.maps.event.trigger(this.gMapContext.map, "resize");
24390         
24391         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24392         
24393         this.fireEvent('resize', this);
24394     },
24395     
24396     setPositionByLatLng: function(latitude, longitude)
24397     {
24398         this.setPosition(new google.maps.LatLng(latitude, longitude));
24399     },
24400     
24401     getCurrentPosition: function() 
24402     {
24403         return {
24404             latitude: this.gMapContext.location.lat(),
24405             longitude: this.gMapContext.location.lng()
24406         };
24407     },
24408     
24409     getAddressName: function() 
24410     {
24411         return this.gMapContext.locationName;
24412     },
24413     
24414     getAddressComponents: function() 
24415     {
24416         return this.gMapContext.addressComponents;
24417     },
24418     
24419     address_component_from_google_geocode: function(address_components) 
24420     {
24421         var result = {};
24422         
24423         for (var i = 0; i < address_components.length; i++) {
24424             var component = address_components[i];
24425             if (component.types.indexOf("postal_code") >= 0) {
24426                 result.postalCode = component.short_name;
24427             } else if (component.types.indexOf("street_number") >= 0) {
24428                 result.streetNumber = component.short_name;
24429             } else if (component.types.indexOf("route") >= 0) {
24430                 result.streetName = component.short_name;
24431             } else if (component.types.indexOf("neighborhood") >= 0) {
24432                 result.city = component.short_name;
24433             } else if (component.types.indexOf("locality") >= 0) {
24434                 result.city = component.short_name;
24435             } else if (component.types.indexOf("sublocality") >= 0) {
24436                 result.district = component.short_name;
24437             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24438                 result.stateOrProvince = component.short_name;
24439             } else if (component.types.indexOf("country") >= 0) {
24440                 result.country = component.short_name;
24441             }
24442         }
24443         
24444         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24445         result.addressLine2 = "";
24446         return result;
24447     },
24448     
24449     setZoomLevel: function(zoom)
24450     {
24451         this.gMapContext.map.setZoom(zoom);
24452     },
24453     
24454     show: function()
24455     {
24456         if(!this.el){
24457             return;
24458         }
24459         
24460         this.el.show();
24461         
24462         this.resize();
24463         
24464         this.fireEvent('show', this);
24465     },
24466     
24467     hide: function()
24468     {
24469         if(!this.el){
24470             return;
24471         }
24472         
24473         this.el.hide();
24474         
24475         this.fireEvent('hide', this);
24476     }
24477     
24478 });
24479
24480 Roo.apply(Roo.bootstrap.LocationPicker, {
24481     
24482     OverlayView : function(map, options)
24483     {
24484         options = options || {};
24485         
24486         this.setMap(map);
24487     }
24488     
24489     
24490 });/*
24491  * - LGPL
24492  *
24493  * Alert
24494  * 
24495  */
24496
24497 /**
24498  * @class Roo.bootstrap.Alert
24499  * @extends Roo.bootstrap.Component
24500  * Bootstrap Alert class
24501  * @cfg {String} title The title of alert
24502  * @cfg {String} html The content of alert
24503  * @cfg {String} weight (  success | info | warning | danger )
24504  * @cfg {String} faicon font-awesomeicon
24505  * 
24506  * @constructor
24507  * Create a new alert
24508  * @param {Object} config The config object
24509  */
24510
24511
24512 Roo.bootstrap.Alert = function(config){
24513     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24514     
24515 };
24516
24517 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24518     
24519     title: '',
24520     html: '',
24521     weight: false,
24522     faicon: false,
24523     
24524     getAutoCreate : function()
24525     {
24526         
24527         var cfg = {
24528             tag : 'div',
24529             cls : 'alert',
24530             cn : [
24531                 {
24532                     tag : 'i',
24533                     cls : 'roo-alert-icon'
24534                     
24535                 },
24536                 {
24537                     tag : 'b',
24538                     cls : 'roo-alert-title',
24539                     html : this.title
24540                 },
24541                 {
24542                     tag : 'span',
24543                     cls : 'roo-alert-text',
24544                     html : this.html
24545                 }
24546             ]
24547         };
24548         
24549         if(this.faicon){
24550             cfg.cn[0].cls += ' fa ' + this.faicon;
24551         }
24552         
24553         if(this.weight){
24554             cfg.cls += ' alert-' + this.weight;
24555         }
24556         
24557         return cfg;
24558     },
24559     
24560     initEvents: function() 
24561     {
24562         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24563     },
24564     
24565     setTitle : function(str)
24566     {
24567         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24568     },
24569     
24570     setText : function(str)
24571     {
24572         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24573     },
24574     
24575     setWeight : function(weight)
24576     {
24577         if(this.weight){
24578             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24579         }
24580         
24581         this.weight = weight;
24582         
24583         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24584     },
24585     
24586     setIcon : function(icon)
24587     {
24588         if(this.faicon){
24589             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24590         }
24591         
24592         this.faicon = icon;
24593         
24594         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24595     },
24596     
24597     hide: function() 
24598     {
24599         this.el.hide();   
24600     },
24601     
24602     show: function() 
24603     {  
24604         this.el.show();   
24605     }
24606     
24607 });
24608
24609  
24610 /*
24611 * Licence: LGPL
24612 */
24613
24614 /**
24615  * @class Roo.bootstrap.UploadCropbox
24616  * @extends Roo.bootstrap.Component
24617  * Bootstrap UploadCropbox class
24618  * @cfg {String} emptyText show when image has been loaded
24619  * @cfg {String} rotateNotify show when image too small to rotate
24620  * @cfg {Number} errorTimeout default 3000
24621  * @cfg {Number} minWidth default 300
24622  * @cfg {Number} minHeight default 300
24623  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24624  * @cfg {Boolean} isDocument (true|false) default false
24625  * @cfg {String} url action url
24626  * @cfg {String} paramName default 'imageUpload'
24627  * @cfg {String} method default POST
24628  * @cfg {Boolean} loadMask (true|false) default true
24629  * @cfg {Boolean} loadingText default 'Loading...'
24630  * 
24631  * @constructor
24632  * Create a new UploadCropbox
24633  * @param {Object} config The config object
24634  */
24635
24636 Roo.bootstrap.UploadCropbox = function(config){
24637     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24638     
24639     this.addEvents({
24640         /**
24641          * @event beforeselectfile
24642          * Fire before select file
24643          * @param {Roo.bootstrap.UploadCropbox} this
24644          */
24645         "beforeselectfile" : true,
24646         /**
24647          * @event initial
24648          * Fire after initEvent
24649          * @param {Roo.bootstrap.UploadCropbox} this
24650          */
24651         "initial" : true,
24652         /**
24653          * @event crop
24654          * Fire after initEvent
24655          * @param {Roo.bootstrap.UploadCropbox} this
24656          * @param {String} data
24657          */
24658         "crop" : true,
24659         /**
24660          * @event prepare
24661          * Fire when preparing the file data
24662          * @param {Roo.bootstrap.UploadCropbox} this
24663          * @param {Object} file
24664          */
24665         "prepare" : true,
24666         /**
24667          * @event exception
24668          * Fire when get exception
24669          * @param {Roo.bootstrap.UploadCropbox} this
24670          * @param {XMLHttpRequest} xhr
24671          */
24672         "exception" : true,
24673         /**
24674          * @event beforeloadcanvas
24675          * Fire before load the canvas
24676          * @param {Roo.bootstrap.UploadCropbox} this
24677          * @param {String} src
24678          */
24679         "beforeloadcanvas" : true,
24680         /**
24681          * @event trash
24682          * Fire when trash image
24683          * @param {Roo.bootstrap.UploadCropbox} this
24684          */
24685         "trash" : true,
24686         /**
24687          * @event download
24688          * Fire when download the image
24689          * @param {Roo.bootstrap.UploadCropbox} this
24690          */
24691         "download" : true,
24692         /**
24693          * @event footerbuttonclick
24694          * Fire when footerbuttonclick
24695          * @param {Roo.bootstrap.UploadCropbox} this
24696          * @param {String} type
24697          */
24698         "footerbuttonclick" : true,
24699         /**
24700          * @event resize
24701          * Fire when resize
24702          * @param {Roo.bootstrap.UploadCropbox} this
24703          */
24704         "resize" : true,
24705         /**
24706          * @event rotate
24707          * Fire when rotate the image
24708          * @param {Roo.bootstrap.UploadCropbox} this
24709          * @param {String} pos
24710          */
24711         "rotate" : true,
24712         /**
24713          * @event inspect
24714          * Fire when inspect the file
24715          * @param {Roo.bootstrap.UploadCropbox} this
24716          * @param {Object} file
24717          */
24718         "inspect" : true,
24719         /**
24720          * @event upload
24721          * Fire when xhr upload the file
24722          * @param {Roo.bootstrap.UploadCropbox} this
24723          * @param {Object} data
24724          */
24725         "upload" : true,
24726         /**
24727          * @event arrange
24728          * Fire when arrange the file data
24729          * @param {Roo.bootstrap.UploadCropbox} this
24730          * @param {Object} formData
24731          */
24732         "arrange" : true
24733     });
24734     
24735     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24736 };
24737
24738 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24739     
24740     emptyText : 'Click to upload image',
24741     rotateNotify : 'Image is too small to rotate',
24742     errorTimeout : 3000,
24743     scale : 0,
24744     baseScale : 1,
24745     rotate : 0,
24746     dragable : false,
24747     pinching : false,
24748     mouseX : 0,
24749     mouseY : 0,
24750     cropData : false,
24751     minWidth : 300,
24752     minHeight : 300,
24753     file : false,
24754     exif : {},
24755     baseRotate : 1,
24756     cropType : 'image/jpeg',
24757     buttons : false,
24758     canvasLoaded : false,
24759     isDocument : false,
24760     method : 'POST',
24761     paramName : 'imageUpload',
24762     loadMask : true,
24763     loadingText : 'Loading...',
24764     maskEl : false,
24765     
24766     getAutoCreate : function()
24767     {
24768         var cfg = {
24769             tag : 'div',
24770             cls : 'roo-upload-cropbox',
24771             cn : [
24772                 {
24773                     tag : 'input',
24774                     cls : 'roo-upload-cropbox-selector',
24775                     type : 'file'
24776                 },
24777                 {
24778                     tag : 'div',
24779                     cls : 'roo-upload-cropbox-body',
24780                     style : 'cursor:pointer',
24781                     cn : [
24782                         {
24783                             tag : 'div',
24784                             cls : 'roo-upload-cropbox-preview'
24785                         },
24786                         {
24787                             tag : 'div',
24788                             cls : 'roo-upload-cropbox-thumb'
24789                         },
24790                         {
24791                             tag : 'div',
24792                             cls : 'roo-upload-cropbox-empty-notify',
24793                             html : this.emptyText
24794                         },
24795                         {
24796                             tag : 'div',
24797                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24798                             html : this.rotateNotify
24799                         }
24800                     ]
24801                 },
24802                 {
24803                     tag : 'div',
24804                     cls : 'roo-upload-cropbox-footer',
24805                     cn : {
24806                         tag : 'div',
24807                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24808                         cn : []
24809                     }
24810                 }
24811             ]
24812         };
24813         
24814         return cfg;
24815     },
24816     
24817     onRender : function(ct, position)
24818     {
24819         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24820         
24821         if (this.buttons.length) {
24822             
24823             Roo.each(this.buttons, function(bb) {
24824                 
24825                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24826                 
24827                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24828                 
24829             }, this);
24830         }
24831         
24832         if(this.loadMask){
24833             this.maskEl = this.el;
24834         }
24835     },
24836     
24837     initEvents : function()
24838     {
24839         this.urlAPI = (window.createObjectURL && window) || 
24840                                 (window.URL && URL.revokeObjectURL && URL) || 
24841                                 (window.webkitURL && webkitURL);
24842                         
24843         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24844         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24845         
24846         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24847         this.selectorEl.hide();
24848         
24849         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24850         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24851         
24852         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24853         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24854         this.thumbEl.hide();
24855         
24856         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24857         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24858         
24859         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24860         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24861         this.errorEl.hide();
24862         
24863         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24864         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24865         this.footerEl.hide();
24866         
24867         this.setThumbBoxSize();
24868         
24869         this.bind();
24870         
24871         this.resize();
24872         
24873         this.fireEvent('initial', this);
24874     },
24875
24876     bind : function()
24877     {
24878         var _this = this;
24879         
24880         window.addEventListener("resize", function() { _this.resize(); } );
24881         
24882         this.bodyEl.on('click', this.beforeSelectFile, this);
24883         
24884         if(Roo.isTouch){
24885             this.bodyEl.on('touchstart', this.onTouchStart, this);
24886             this.bodyEl.on('touchmove', this.onTouchMove, this);
24887             this.bodyEl.on('touchend', this.onTouchEnd, this);
24888         }
24889         
24890         if(!Roo.isTouch){
24891             this.bodyEl.on('mousedown', this.onMouseDown, this);
24892             this.bodyEl.on('mousemove', this.onMouseMove, this);
24893             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24894             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24895             Roo.get(document).on('mouseup', this.onMouseUp, this);
24896         }
24897         
24898         this.selectorEl.on('change', this.onFileSelected, this);
24899     },
24900     
24901     reset : function()
24902     {    
24903         this.scale = 0;
24904         this.baseScale = 1;
24905         this.rotate = 0;
24906         this.baseRotate = 1;
24907         this.dragable = false;
24908         this.pinching = false;
24909         this.mouseX = 0;
24910         this.mouseY = 0;
24911         this.cropData = false;
24912         this.notifyEl.dom.innerHTML = this.emptyText;
24913         
24914         this.selectorEl.dom.value = '';
24915         
24916     },
24917     
24918     resize : function()
24919     {
24920         if(this.fireEvent('resize', this) != false){
24921             this.setThumbBoxPosition();
24922             this.setCanvasPosition();
24923         }
24924     },
24925     
24926     onFooterButtonClick : function(e, el, o, type)
24927     {
24928         switch (type) {
24929             case 'rotate-left' :
24930                 this.onRotateLeft(e);
24931                 break;
24932             case 'rotate-right' :
24933                 this.onRotateRight(e);
24934                 break;
24935             case 'picture' :
24936                 this.beforeSelectFile(e);
24937                 break;
24938             case 'trash' :
24939                 this.trash(e);
24940                 break;
24941             case 'crop' :
24942                 this.crop(e);
24943                 break;
24944             case 'download' :
24945                 this.download(e);
24946                 break;
24947             default :
24948                 break;
24949         }
24950         
24951         this.fireEvent('footerbuttonclick', this, type);
24952     },
24953     
24954     beforeSelectFile : function(e)
24955     {
24956         e.preventDefault();
24957         
24958         if(this.fireEvent('beforeselectfile', this) != false){
24959             this.selectorEl.dom.click();
24960         }
24961     },
24962     
24963     onFileSelected : function(e)
24964     {
24965         e.preventDefault();
24966         
24967         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24968             return;
24969         }
24970         
24971         var file = this.selectorEl.dom.files[0];
24972         
24973         if(this.fireEvent('inspect', this, file) != false){
24974             this.prepare(file);
24975         }
24976         
24977     },
24978     
24979     trash : function(e)
24980     {
24981         this.fireEvent('trash', this);
24982     },
24983     
24984     download : function(e)
24985     {
24986         this.fireEvent('download', this);
24987     },
24988     
24989     loadCanvas : function(src)
24990     {   
24991         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24992             
24993             this.reset();
24994             
24995             this.imageEl = document.createElement('img');
24996             
24997             var _this = this;
24998             
24999             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25000             
25001             this.imageEl.src = src;
25002         }
25003     },
25004     
25005     onLoadCanvas : function()
25006     {   
25007         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25008         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25009         
25010         this.bodyEl.un('click', this.beforeSelectFile, this);
25011         
25012         this.notifyEl.hide();
25013         this.thumbEl.show();
25014         this.footerEl.show();
25015         
25016         this.baseRotateLevel();
25017         
25018         if(this.isDocument){
25019             this.setThumbBoxSize();
25020         }
25021         
25022         this.setThumbBoxPosition();
25023         
25024         this.baseScaleLevel();
25025         
25026         this.draw();
25027         
25028         this.resize();
25029         
25030         this.canvasLoaded = true;
25031         
25032         if(this.loadMask){
25033             this.maskEl.unmask();
25034         }
25035         
25036     },
25037     
25038     setCanvasPosition : function()
25039     {   
25040         if(!this.canvasEl){
25041             return;
25042         }
25043         
25044         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25045         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25046         
25047         this.previewEl.setLeft(pw);
25048         this.previewEl.setTop(ph);
25049         
25050     },
25051     
25052     onMouseDown : function(e)
25053     {   
25054         e.stopEvent();
25055         
25056         this.dragable = true;
25057         this.pinching = false;
25058         
25059         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25060             this.dragable = false;
25061             return;
25062         }
25063         
25064         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25065         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25066         
25067     },
25068     
25069     onMouseMove : function(e)
25070     {   
25071         e.stopEvent();
25072         
25073         if(!this.canvasLoaded){
25074             return;
25075         }
25076         
25077         if (!this.dragable){
25078             return;
25079         }
25080         
25081         var minX = Math.ceil(this.thumbEl.getLeft(true));
25082         var minY = Math.ceil(this.thumbEl.getTop(true));
25083         
25084         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25085         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25086         
25087         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25088         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25089         
25090         x = x - this.mouseX;
25091         y = y - this.mouseY;
25092         
25093         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25094         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25095         
25096         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25097         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25098         
25099         this.previewEl.setLeft(bgX);
25100         this.previewEl.setTop(bgY);
25101         
25102         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25103         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25104     },
25105     
25106     onMouseUp : function(e)
25107     {   
25108         e.stopEvent();
25109         
25110         this.dragable = false;
25111     },
25112     
25113     onMouseWheel : function(e)
25114     {   
25115         e.stopEvent();
25116         
25117         this.startScale = this.scale;
25118         
25119         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25120         
25121         if(!this.zoomable()){
25122             this.scale = this.startScale;
25123             return;
25124         }
25125         
25126         this.draw();
25127         
25128         return;
25129     },
25130     
25131     zoomable : function()
25132     {
25133         var minScale = this.thumbEl.getWidth() / this.minWidth;
25134         
25135         if(this.minWidth < this.minHeight){
25136             minScale = this.thumbEl.getHeight() / this.minHeight;
25137         }
25138         
25139         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25140         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25141         
25142         if(
25143                 this.isDocument &&
25144                 (this.rotate == 0 || this.rotate == 180) && 
25145                 (
25146                     width > this.imageEl.OriginWidth || 
25147                     height > this.imageEl.OriginHeight ||
25148                     (width < this.minWidth && height < this.minHeight)
25149                 )
25150         ){
25151             return false;
25152         }
25153         
25154         if(
25155                 this.isDocument &&
25156                 (this.rotate == 90 || this.rotate == 270) && 
25157                 (
25158                     width > this.imageEl.OriginWidth || 
25159                     height > this.imageEl.OriginHeight ||
25160                     (width < this.minHeight && height < this.minWidth)
25161                 )
25162         ){
25163             return false;
25164         }
25165         
25166         if(
25167                 !this.isDocument &&
25168                 (this.rotate == 0 || this.rotate == 180) && 
25169                 (
25170                     width < this.minWidth || 
25171                     width > this.imageEl.OriginWidth || 
25172                     height < this.minHeight || 
25173                     height > this.imageEl.OriginHeight
25174                 )
25175         ){
25176             return false;
25177         }
25178         
25179         if(
25180                 !this.isDocument &&
25181                 (this.rotate == 90 || this.rotate == 270) && 
25182                 (
25183                     width < this.minHeight || 
25184                     width > this.imageEl.OriginWidth || 
25185                     height < this.minWidth || 
25186                     height > this.imageEl.OriginHeight
25187                 )
25188         ){
25189             return false;
25190         }
25191         
25192         return true;
25193         
25194     },
25195     
25196     onRotateLeft : function(e)
25197     {   
25198         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25199             
25200             var minScale = this.thumbEl.getWidth() / this.minWidth;
25201             
25202             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25203             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25204             
25205             this.startScale = this.scale;
25206             
25207             while (this.getScaleLevel() < minScale){
25208             
25209                 this.scale = this.scale + 1;
25210                 
25211                 if(!this.zoomable()){
25212                     break;
25213                 }
25214                 
25215                 if(
25216                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25217                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25218                 ){
25219                     continue;
25220                 }
25221                 
25222                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25223
25224                 this.draw();
25225                 
25226                 return;
25227             }
25228             
25229             this.scale = this.startScale;
25230             
25231             this.onRotateFail();
25232             
25233             return false;
25234         }
25235         
25236         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25237
25238         if(this.isDocument){
25239             this.setThumbBoxSize();
25240             this.setThumbBoxPosition();
25241             this.setCanvasPosition();
25242         }
25243         
25244         this.draw();
25245         
25246         this.fireEvent('rotate', this, 'left');
25247         
25248     },
25249     
25250     onRotateRight : function(e)
25251     {
25252         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25253             
25254             var minScale = this.thumbEl.getWidth() / this.minWidth;
25255         
25256             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25257             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25258             
25259             this.startScale = this.scale;
25260             
25261             while (this.getScaleLevel() < minScale){
25262             
25263                 this.scale = this.scale + 1;
25264                 
25265                 if(!this.zoomable()){
25266                     break;
25267                 }
25268                 
25269                 if(
25270                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25271                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25272                 ){
25273                     continue;
25274                 }
25275                 
25276                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25277
25278                 this.draw();
25279                 
25280                 return;
25281             }
25282             
25283             this.scale = this.startScale;
25284             
25285             this.onRotateFail();
25286             
25287             return false;
25288         }
25289         
25290         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25291
25292         if(this.isDocument){
25293             this.setThumbBoxSize();
25294             this.setThumbBoxPosition();
25295             this.setCanvasPosition();
25296         }
25297         
25298         this.draw();
25299         
25300         this.fireEvent('rotate', this, 'right');
25301     },
25302     
25303     onRotateFail : function()
25304     {
25305         this.errorEl.show(true);
25306         
25307         var _this = this;
25308         
25309         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25310     },
25311     
25312     draw : function()
25313     {
25314         this.previewEl.dom.innerHTML = '';
25315         
25316         var canvasEl = document.createElement("canvas");
25317         
25318         var contextEl = canvasEl.getContext("2d");
25319         
25320         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25321         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25322         var center = this.imageEl.OriginWidth / 2;
25323         
25324         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25325             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25326             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25327             center = this.imageEl.OriginHeight / 2;
25328         }
25329         
25330         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25331         
25332         contextEl.translate(center, center);
25333         contextEl.rotate(this.rotate * Math.PI / 180);
25334
25335         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25336         
25337         this.canvasEl = document.createElement("canvas");
25338         
25339         this.contextEl = this.canvasEl.getContext("2d");
25340         
25341         switch (this.rotate) {
25342             case 0 :
25343                 
25344                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25345                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25346                 
25347                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25348                 
25349                 break;
25350             case 90 : 
25351                 
25352                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25353                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25354                 
25355                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25356                     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);
25357                     break;
25358                 }
25359                 
25360                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25361                 
25362                 break;
25363             case 180 :
25364                 
25365                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25366                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25367                 
25368                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25369                     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);
25370                     break;
25371                 }
25372                 
25373                 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);
25374                 
25375                 break;
25376             case 270 :
25377                 
25378                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25379                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25380         
25381                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25382                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25383                     break;
25384                 }
25385                 
25386                 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);
25387                 
25388                 break;
25389             default : 
25390                 break;
25391         }
25392         
25393         this.previewEl.appendChild(this.canvasEl);
25394         
25395         this.setCanvasPosition();
25396     },
25397     
25398     crop : function()
25399     {
25400         if(!this.canvasLoaded){
25401             return;
25402         }
25403         
25404         var imageCanvas = document.createElement("canvas");
25405         
25406         var imageContext = imageCanvas.getContext("2d");
25407         
25408         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25409         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25410         
25411         var center = imageCanvas.width / 2;
25412         
25413         imageContext.translate(center, center);
25414         
25415         imageContext.rotate(this.rotate * Math.PI / 180);
25416         
25417         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25418         
25419         var canvas = document.createElement("canvas");
25420         
25421         var context = canvas.getContext("2d");
25422                 
25423         canvas.width = this.minWidth;
25424         canvas.height = this.minHeight;
25425
25426         switch (this.rotate) {
25427             case 0 :
25428                 
25429                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25430                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25431                 
25432                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25433                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25434                 
25435                 var targetWidth = this.minWidth - 2 * x;
25436                 var targetHeight = this.minHeight - 2 * y;
25437                 
25438                 var scale = 1;
25439                 
25440                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25441                     scale = targetWidth / width;
25442                 }
25443                 
25444                 if(x > 0 && y == 0){
25445                     scale = targetHeight / height;
25446                 }
25447                 
25448                 if(x > 0 && y > 0){
25449                     scale = targetWidth / width;
25450                     
25451                     if(width < height){
25452                         scale = targetHeight / height;
25453                     }
25454                 }
25455                 
25456                 context.scale(scale, scale);
25457                 
25458                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25459                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25460
25461                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25462                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25463
25464                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25465                 
25466                 break;
25467             case 90 : 
25468                 
25469                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25470                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25471                 
25472                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25473                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25474                 
25475                 var targetWidth = this.minWidth - 2 * x;
25476                 var targetHeight = this.minHeight - 2 * y;
25477                 
25478                 var scale = 1;
25479                 
25480                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25481                     scale = targetWidth / width;
25482                 }
25483                 
25484                 if(x > 0 && y == 0){
25485                     scale = targetHeight / height;
25486                 }
25487                 
25488                 if(x > 0 && y > 0){
25489                     scale = targetWidth / width;
25490                     
25491                     if(width < height){
25492                         scale = targetHeight / height;
25493                     }
25494                 }
25495                 
25496                 context.scale(scale, scale);
25497                 
25498                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25499                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25500
25501                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25502                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25503                 
25504                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25505                 
25506                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25507                 
25508                 break;
25509             case 180 :
25510                 
25511                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25512                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25513                 
25514                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25515                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25516                 
25517                 var targetWidth = this.minWidth - 2 * x;
25518                 var targetHeight = this.minHeight - 2 * y;
25519                 
25520                 var scale = 1;
25521                 
25522                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25523                     scale = targetWidth / width;
25524                 }
25525                 
25526                 if(x > 0 && y == 0){
25527                     scale = targetHeight / height;
25528                 }
25529                 
25530                 if(x > 0 && y > 0){
25531                     scale = targetWidth / width;
25532                     
25533                     if(width < height){
25534                         scale = targetHeight / height;
25535                     }
25536                 }
25537                 
25538                 context.scale(scale, scale);
25539                 
25540                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25541                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25542
25543                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25544                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25545
25546                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25547                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25548                 
25549                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25550                 
25551                 break;
25552             case 270 :
25553                 
25554                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25555                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25556                 
25557                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25558                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25559                 
25560                 var targetWidth = this.minWidth - 2 * x;
25561                 var targetHeight = this.minHeight - 2 * y;
25562                 
25563                 var scale = 1;
25564                 
25565                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25566                     scale = targetWidth / width;
25567                 }
25568                 
25569                 if(x > 0 && y == 0){
25570                     scale = targetHeight / height;
25571                 }
25572                 
25573                 if(x > 0 && y > 0){
25574                     scale = targetWidth / width;
25575                     
25576                     if(width < height){
25577                         scale = targetHeight / height;
25578                     }
25579                 }
25580                 
25581                 context.scale(scale, scale);
25582                 
25583                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25584                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25585
25586                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25587                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25588                 
25589                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25590                 
25591                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25592                 
25593                 break;
25594             default : 
25595                 break;
25596         }
25597         
25598         this.cropData = canvas.toDataURL(this.cropType);
25599         
25600         if(this.fireEvent('crop', this, this.cropData) !== false){
25601             this.process(this.file, this.cropData);
25602         }
25603         
25604         return;
25605         
25606     },
25607     
25608     setThumbBoxSize : function()
25609     {
25610         var width, height;
25611         
25612         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25613             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25614             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25615             
25616             this.minWidth = width;
25617             this.minHeight = height;
25618             
25619             if(this.rotate == 90 || this.rotate == 270){
25620                 this.minWidth = height;
25621                 this.minHeight = width;
25622             }
25623         }
25624         
25625         height = 300;
25626         width = Math.ceil(this.minWidth * height / this.minHeight);
25627         
25628         if(this.minWidth > this.minHeight){
25629             width = 300;
25630             height = Math.ceil(this.minHeight * width / this.minWidth);
25631         }
25632         
25633         this.thumbEl.setStyle({
25634             width : width + 'px',
25635             height : height + 'px'
25636         });
25637
25638         return;
25639             
25640     },
25641     
25642     setThumbBoxPosition : function()
25643     {
25644         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25645         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25646         
25647         this.thumbEl.setLeft(x);
25648         this.thumbEl.setTop(y);
25649         
25650     },
25651     
25652     baseRotateLevel : function()
25653     {
25654         this.baseRotate = 1;
25655         
25656         if(
25657                 typeof(this.exif) != 'undefined' &&
25658                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25659                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25660         ){
25661             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25662         }
25663         
25664         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25665         
25666     },
25667     
25668     baseScaleLevel : function()
25669     {
25670         var width, height;
25671         
25672         if(this.isDocument){
25673             
25674             if(this.baseRotate == 6 || this.baseRotate == 8){
25675             
25676                 height = this.thumbEl.getHeight();
25677                 this.baseScale = height / this.imageEl.OriginWidth;
25678
25679                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25680                     width = this.thumbEl.getWidth();
25681                     this.baseScale = width / this.imageEl.OriginHeight;
25682                 }
25683
25684                 return;
25685             }
25686
25687             height = this.thumbEl.getHeight();
25688             this.baseScale = height / this.imageEl.OriginHeight;
25689
25690             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25691                 width = this.thumbEl.getWidth();
25692                 this.baseScale = width / this.imageEl.OriginWidth;
25693             }
25694
25695             return;
25696         }
25697         
25698         if(this.baseRotate == 6 || this.baseRotate == 8){
25699             
25700             width = this.thumbEl.getHeight();
25701             this.baseScale = width / this.imageEl.OriginHeight;
25702             
25703             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25704                 height = this.thumbEl.getWidth();
25705                 this.baseScale = height / this.imageEl.OriginHeight;
25706             }
25707             
25708             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25709                 height = this.thumbEl.getWidth();
25710                 this.baseScale = height / this.imageEl.OriginHeight;
25711                 
25712                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25713                     width = this.thumbEl.getHeight();
25714                     this.baseScale = width / this.imageEl.OriginWidth;
25715                 }
25716             }
25717             
25718             return;
25719         }
25720         
25721         width = this.thumbEl.getWidth();
25722         this.baseScale = width / this.imageEl.OriginWidth;
25723         
25724         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25725             height = this.thumbEl.getHeight();
25726             this.baseScale = height / this.imageEl.OriginHeight;
25727         }
25728         
25729         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25730             
25731             height = this.thumbEl.getHeight();
25732             this.baseScale = height / this.imageEl.OriginHeight;
25733             
25734             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25735                 width = this.thumbEl.getWidth();
25736                 this.baseScale = width / this.imageEl.OriginWidth;
25737             }
25738             
25739         }
25740         
25741         return;
25742     },
25743     
25744     getScaleLevel : function()
25745     {
25746         return this.baseScale * Math.pow(1.1, this.scale);
25747     },
25748     
25749     onTouchStart : function(e)
25750     {
25751         if(!this.canvasLoaded){
25752             this.beforeSelectFile(e);
25753             return;
25754         }
25755         
25756         var touches = e.browserEvent.touches;
25757         
25758         if(!touches){
25759             return;
25760         }
25761         
25762         if(touches.length == 1){
25763             this.onMouseDown(e);
25764             return;
25765         }
25766         
25767         if(touches.length != 2){
25768             return;
25769         }
25770         
25771         var coords = [];
25772         
25773         for(var i = 0, finger; finger = touches[i]; i++){
25774             coords.push(finger.pageX, finger.pageY);
25775         }
25776         
25777         var x = Math.pow(coords[0] - coords[2], 2);
25778         var y = Math.pow(coords[1] - coords[3], 2);
25779         
25780         this.startDistance = Math.sqrt(x + y);
25781         
25782         this.startScale = this.scale;
25783         
25784         this.pinching = true;
25785         this.dragable = false;
25786         
25787     },
25788     
25789     onTouchMove : function(e)
25790     {
25791         if(!this.pinching && !this.dragable){
25792             return;
25793         }
25794         
25795         var touches = e.browserEvent.touches;
25796         
25797         if(!touches){
25798             return;
25799         }
25800         
25801         if(this.dragable){
25802             this.onMouseMove(e);
25803             return;
25804         }
25805         
25806         var coords = [];
25807         
25808         for(var i = 0, finger; finger = touches[i]; i++){
25809             coords.push(finger.pageX, finger.pageY);
25810         }
25811         
25812         var x = Math.pow(coords[0] - coords[2], 2);
25813         var y = Math.pow(coords[1] - coords[3], 2);
25814         
25815         this.endDistance = Math.sqrt(x + y);
25816         
25817         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25818         
25819         if(!this.zoomable()){
25820             this.scale = this.startScale;
25821             return;
25822         }
25823         
25824         this.draw();
25825         
25826     },
25827     
25828     onTouchEnd : function(e)
25829     {
25830         this.pinching = false;
25831         this.dragable = false;
25832         
25833     },
25834     
25835     process : function(file, crop)
25836     {
25837         if(this.loadMask){
25838             this.maskEl.mask(this.loadingText);
25839         }
25840         
25841         this.xhr = new XMLHttpRequest();
25842         
25843         file.xhr = this.xhr;
25844
25845         this.xhr.open(this.method, this.url, true);
25846         
25847         var headers = {
25848             "Accept": "application/json",
25849             "Cache-Control": "no-cache",
25850             "X-Requested-With": "XMLHttpRequest"
25851         };
25852         
25853         for (var headerName in headers) {
25854             var headerValue = headers[headerName];
25855             if (headerValue) {
25856                 this.xhr.setRequestHeader(headerName, headerValue);
25857             }
25858         }
25859         
25860         var _this = this;
25861         
25862         this.xhr.onload = function()
25863         {
25864             _this.xhrOnLoad(_this.xhr);
25865         }
25866         
25867         this.xhr.onerror = function()
25868         {
25869             _this.xhrOnError(_this.xhr);
25870         }
25871         
25872         var formData = new FormData();
25873
25874         formData.append('returnHTML', 'NO');
25875         
25876         if(crop){
25877             formData.append('crop', crop);
25878         }
25879         
25880         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25881             formData.append(this.paramName, file, file.name);
25882         }
25883         
25884         if(typeof(file.filename) != 'undefined'){
25885             formData.append('filename', file.filename);
25886         }
25887         
25888         if(typeof(file.mimetype) != 'undefined'){
25889             formData.append('mimetype', file.mimetype);
25890         }
25891         
25892         if(this.fireEvent('arrange', this, formData) != false){
25893             this.xhr.send(formData);
25894         };
25895     },
25896     
25897     xhrOnLoad : function(xhr)
25898     {
25899         if(this.loadMask){
25900             this.maskEl.unmask();
25901         }
25902         
25903         if (xhr.readyState !== 4) {
25904             this.fireEvent('exception', this, xhr);
25905             return;
25906         }
25907
25908         var response = Roo.decode(xhr.responseText);
25909         
25910         if(!response.success){
25911             this.fireEvent('exception', this, xhr);
25912             return;
25913         }
25914         
25915         var response = Roo.decode(xhr.responseText);
25916         
25917         this.fireEvent('upload', this, response);
25918         
25919     },
25920     
25921     xhrOnError : function()
25922     {
25923         if(this.loadMask){
25924             this.maskEl.unmask();
25925         }
25926         
25927         Roo.log('xhr on error');
25928         
25929         var response = Roo.decode(xhr.responseText);
25930           
25931         Roo.log(response);
25932         
25933     },
25934     
25935     prepare : function(file)
25936     {   
25937         if(this.loadMask){
25938             this.maskEl.mask(this.loadingText);
25939         }
25940         
25941         this.file = false;
25942         this.exif = {};
25943         
25944         if(typeof(file) === 'string'){
25945             this.loadCanvas(file);
25946             return;
25947         }
25948         
25949         if(!file || !this.urlAPI){
25950             return;
25951         }
25952         
25953         this.file = file;
25954         this.cropType = file.type;
25955         
25956         var _this = this;
25957         
25958         if(this.fireEvent('prepare', this, this.file) != false){
25959             
25960             var reader = new FileReader();
25961             
25962             reader.onload = function (e) {
25963                 if (e.target.error) {
25964                     Roo.log(e.target.error);
25965                     return;
25966                 }
25967                 
25968                 var buffer = e.target.result,
25969                     dataView = new DataView(buffer),
25970                     offset = 2,
25971                     maxOffset = dataView.byteLength - 4,
25972                     markerBytes,
25973                     markerLength;
25974                 
25975                 if (dataView.getUint16(0) === 0xffd8) {
25976                     while (offset < maxOffset) {
25977                         markerBytes = dataView.getUint16(offset);
25978                         
25979                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25980                             markerLength = dataView.getUint16(offset + 2) + 2;
25981                             if (offset + markerLength > dataView.byteLength) {
25982                                 Roo.log('Invalid meta data: Invalid segment size.');
25983                                 break;
25984                             }
25985                             
25986                             if(markerBytes == 0xffe1){
25987                                 _this.parseExifData(
25988                                     dataView,
25989                                     offset,
25990                                     markerLength
25991                                 );
25992                             }
25993                             
25994                             offset += markerLength;
25995                             
25996                             continue;
25997                         }
25998                         
25999                         break;
26000                     }
26001                     
26002                 }
26003                 
26004                 var url = _this.urlAPI.createObjectURL(_this.file);
26005                 
26006                 _this.loadCanvas(url);
26007                 
26008                 return;
26009             }
26010             
26011             reader.readAsArrayBuffer(this.file);
26012             
26013         }
26014         
26015     },
26016     
26017     parseExifData : function(dataView, offset, length)
26018     {
26019         var tiffOffset = offset + 10,
26020             littleEndian,
26021             dirOffset;
26022     
26023         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26024             // No Exif data, might be XMP data instead
26025             return;
26026         }
26027         
26028         // Check for the ASCII code for "Exif" (0x45786966):
26029         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26030             // No Exif data, might be XMP data instead
26031             return;
26032         }
26033         if (tiffOffset + 8 > dataView.byteLength) {
26034             Roo.log('Invalid Exif data: Invalid segment size.');
26035             return;
26036         }
26037         // Check for the two null bytes:
26038         if (dataView.getUint16(offset + 8) !== 0x0000) {
26039             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26040             return;
26041         }
26042         // Check the byte alignment:
26043         switch (dataView.getUint16(tiffOffset)) {
26044         case 0x4949:
26045             littleEndian = true;
26046             break;
26047         case 0x4D4D:
26048             littleEndian = false;
26049             break;
26050         default:
26051             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26052             return;
26053         }
26054         // Check for the TIFF tag marker (0x002A):
26055         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26056             Roo.log('Invalid Exif data: Missing TIFF marker.');
26057             return;
26058         }
26059         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26060         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26061         
26062         this.parseExifTags(
26063             dataView,
26064             tiffOffset,
26065             tiffOffset + dirOffset,
26066             littleEndian
26067         );
26068     },
26069     
26070     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26071     {
26072         var tagsNumber,
26073             dirEndOffset,
26074             i;
26075         if (dirOffset + 6 > dataView.byteLength) {
26076             Roo.log('Invalid Exif data: Invalid directory offset.');
26077             return;
26078         }
26079         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26080         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26081         if (dirEndOffset + 4 > dataView.byteLength) {
26082             Roo.log('Invalid Exif data: Invalid directory size.');
26083             return;
26084         }
26085         for (i = 0; i < tagsNumber; i += 1) {
26086             this.parseExifTag(
26087                 dataView,
26088                 tiffOffset,
26089                 dirOffset + 2 + 12 * i, // tag offset
26090                 littleEndian
26091             );
26092         }
26093         // Return the offset to the next directory:
26094         return dataView.getUint32(dirEndOffset, littleEndian);
26095     },
26096     
26097     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26098     {
26099         var tag = dataView.getUint16(offset, littleEndian);
26100         
26101         this.exif[tag] = this.getExifValue(
26102             dataView,
26103             tiffOffset,
26104             offset,
26105             dataView.getUint16(offset + 2, littleEndian), // tag type
26106             dataView.getUint32(offset + 4, littleEndian), // tag length
26107             littleEndian
26108         );
26109     },
26110     
26111     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26112     {
26113         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26114             tagSize,
26115             dataOffset,
26116             values,
26117             i,
26118             str,
26119             c;
26120     
26121         if (!tagType) {
26122             Roo.log('Invalid Exif data: Invalid tag type.');
26123             return;
26124         }
26125         
26126         tagSize = tagType.size * length;
26127         // Determine if the value is contained in the dataOffset bytes,
26128         // or if the value at the dataOffset is a pointer to the actual data:
26129         dataOffset = tagSize > 4 ?
26130                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26131         if (dataOffset + tagSize > dataView.byteLength) {
26132             Roo.log('Invalid Exif data: Invalid data offset.');
26133             return;
26134         }
26135         if (length === 1) {
26136             return tagType.getValue(dataView, dataOffset, littleEndian);
26137         }
26138         values = [];
26139         for (i = 0; i < length; i += 1) {
26140             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26141         }
26142         
26143         if (tagType.ascii) {
26144             str = '';
26145             // Concatenate the chars:
26146             for (i = 0; i < values.length; i += 1) {
26147                 c = values[i];
26148                 // Ignore the terminating NULL byte(s):
26149                 if (c === '\u0000') {
26150                     break;
26151                 }
26152                 str += c;
26153             }
26154             return str;
26155         }
26156         return values;
26157     }
26158     
26159 });
26160
26161 Roo.apply(Roo.bootstrap.UploadCropbox, {
26162     tags : {
26163         'Orientation': 0x0112
26164     },
26165     
26166     Orientation: {
26167             1: 0, //'top-left',
26168 //            2: 'top-right',
26169             3: 180, //'bottom-right',
26170 //            4: 'bottom-left',
26171 //            5: 'left-top',
26172             6: 90, //'right-top',
26173 //            7: 'right-bottom',
26174             8: 270 //'left-bottom'
26175     },
26176     
26177     exifTagTypes : {
26178         // byte, 8-bit unsigned int:
26179         1: {
26180             getValue: function (dataView, dataOffset) {
26181                 return dataView.getUint8(dataOffset);
26182             },
26183             size: 1
26184         },
26185         // ascii, 8-bit byte:
26186         2: {
26187             getValue: function (dataView, dataOffset) {
26188                 return String.fromCharCode(dataView.getUint8(dataOffset));
26189             },
26190             size: 1,
26191             ascii: true
26192         },
26193         // short, 16 bit int:
26194         3: {
26195             getValue: function (dataView, dataOffset, littleEndian) {
26196                 return dataView.getUint16(dataOffset, littleEndian);
26197             },
26198             size: 2
26199         },
26200         // long, 32 bit int:
26201         4: {
26202             getValue: function (dataView, dataOffset, littleEndian) {
26203                 return dataView.getUint32(dataOffset, littleEndian);
26204             },
26205             size: 4
26206         },
26207         // rational = two long values, first is numerator, second is denominator:
26208         5: {
26209             getValue: function (dataView, dataOffset, littleEndian) {
26210                 return dataView.getUint32(dataOffset, littleEndian) /
26211                     dataView.getUint32(dataOffset + 4, littleEndian);
26212             },
26213             size: 8
26214         },
26215         // slong, 32 bit signed int:
26216         9: {
26217             getValue: function (dataView, dataOffset, littleEndian) {
26218                 return dataView.getInt32(dataOffset, littleEndian);
26219             },
26220             size: 4
26221         },
26222         // srational, two slongs, first is numerator, second is denominator:
26223         10: {
26224             getValue: function (dataView, dataOffset, littleEndian) {
26225                 return dataView.getInt32(dataOffset, littleEndian) /
26226                     dataView.getInt32(dataOffset + 4, littleEndian);
26227             },
26228             size: 8
26229         }
26230     },
26231     
26232     footer : {
26233         STANDARD : [
26234             {
26235                 tag : 'div',
26236                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26237                 action : 'rotate-left',
26238                 cn : [
26239                     {
26240                         tag : 'button',
26241                         cls : 'btn btn-default',
26242                         html : '<i class="fa fa-undo"></i>'
26243                     }
26244                 ]
26245             },
26246             {
26247                 tag : 'div',
26248                 cls : 'btn-group roo-upload-cropbox-picture',
26249                 action : 'picture',
26250                 cn : [
26251                     {
26252                         tag : 'button',
26253                         cls : 'btn btn-default',
26254                         html : '<i class="fa fa-picture-o"></i>'
26255                     }
26256                 ]
26257             },
26258             {
26259                 tag : 'div',
26260                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26261                 action : 'rotate-right',
26262                 cn : [
26263                     {
26264                         tag : 'button',
26265                         cls : 'btn btn-default',
26266                         html : '<i class="fa fa-repeat"></i>'
26267                     }
26268                 ]
26269             }
26270         ],
26271         DOCUMENT : [
26272             {
26273                 tag : 'div',
26274                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26275                 action : 'rotate-left',
26276                 cn : [
26277                     {
26278                         tag : 'button',
26279                         cls : 'btn btn-default',
26280                         html : '<i class="fa fa-undo"></i>'
26281                     }
26282                 ]
26283             },
26284             {
26285                 tag : 'div',
26286                 cls : 'btn-group roo-upload-cropbox-download',
26287                 action : 'download',
26288                 cn : [
26289                     {
26290                         tag : 'button',
26291                         cls : 'btn btn-default',
26292                         html : '<i class="fa fa-download"></i>'
26293                     }
26294                 ]
26295             },
26296             {
26297                 tag : 'div',
26298                 cls : 'btn-group roo-upload-cropbox-crop',
26299                 action : 'crop',
26300                 cn : [
26301                     {
26302                         tag : 'button',
26303                         cls : 'btn btn-default',
26304                         html : '<i class="fa fa-crop"></i>'
26305                     }
26306                 ]
26307             },
26308             {
26309                 tag : 'div',
26310                 cls : 'btn-group roo-upload-cropbox-trash',
26311                 action : 'trash',
26312                 cn : [
26313                     {
26314                         tag : 'button',
26315                         cls : 'btn btn-default',
26316                         html : '<i class="fa fa-trash"></i>'
26317                     }
26318                 ]
26319             },
26320             {
26321                 tag : 'div',
26322                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26323                 action : 'rotate-right',
26324                 cn : [
26325                     {
26326                         tag : 'button',
26327                         cls : 'btn btn-default',
26328                         html : '<i class="fa fa-repeat"></i>'
26329                     }
26330                 ]
26331             }
26332         ],
26333         ROTATOR : [
26334             {
26335                 tag : 'div',
26336                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26337                 action : 'rotate-left',
26338                 cn : [
26339                     {
26340                         tag : 'button',
26341                         cls : 'btn btn-default',
26342                         html : '<i class="fa fa-undo"></i>'
26343                     }
26344                 ]
26345             },
26346             {
26347                 tag : 'div',
26348                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26349                 action : 'rotate-right',
26350                 cn : [
26351                     {
26352                         tag : 'button',
26353                         cls : 'btn btn-default',
26354                         html : '<i class="fa fa-repeat"></i>'
26355                     }
26356                 ]
26357             }
26358         ]
26359     }
26360 });
26361
26362 /*
26363 * Licence: LGPL
26364 */
26365
26366 /**
26367  * @class Roo.bootstrap.DocumentManager
26368  * @extends Roo.bootstrap.Component
26369  * Bootstrap DocumentManager class
26370  * @cfg {String} paramName default 'imageUpload'
26371  * @cfg {String} method default POST
26372  * @cfg {String} url action url
26373  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26374  * @cfg {Boolean} multiple multiple upload default true
26375  * @cfg {Number} thumbSize default 300
26376  * @cfg {String} fieldLabel
26377  * @cfg {Number} labelWidth default 4
26378  * @cfg {String} labelAlign (left|top) default left
26379  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26380  * 
26381  * @constructor
26382  * Create a new DocumentManager
26383  * @param {Object} config The config object
26384  */
26385
26386 Roo.bootstrap.DocumentManager = function(config){
26387     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26388     
26389     this.addEvents({
26390         /**
26391          * @event initial
26392          * Fire when initial the DocumentManager
26393          * @param {Roo.bootstrap.DocumentManager} this
26394          */
26395         "initial" : true,
26396         /**
26397          * @event inspect
26398          * inspect selected file
26399          * @param {Roo.bootstrap.DocumentManager} this
26400          * @param {File} file
26401          */
26402         "inspect" : true,
26403         /**
26404          * @event exception
26405          * Fire when xhr load exception
26406          * @param {Roo.bootstrap.DocumentManager} this
26407          * @param {XMLHttpRequest} xhr
26408          */
26409         "exception" : true,
26410         /**
26411          * @event prepare
26412          * prepare the form data
26413          * @param {Roo.bootstrap.DocumentManager} this
26414          * @param {Object} formData
26415          */
26416         "prepare" : true,
26417         /**
26418          * @event remove
26419          * Fire when remove the file
26420          * @param {Roo.bootstrap.DocumentManager} this
26421          * @param {Object} file
26422          */
26423         "remove" : true,
26424         /**
26425          * @event refresh
26426          * Fire after refresh the file
26427          * @param {Roo.bootstrap.DocumentManager} this
26428          */
26429         "refresh" : true,
26430         /**
26431          * @event click
26432          * Fire after click the image
26433          * @param {Roo.bootstrap.DocumentManager} this
26434          * @param {Object} file
26435          */
26436         "click" : true,
26437         /**
26438          * @event edit
26439          * Fire when upload a image and editable set to true
26440          * @param {Roo.bootstrap.DocumentManager} this
26441          * @param {Object} file
26442          */
26443         "edit" : true,
26444         /**
26445          * @event beforeselectfile
26446          * Fire before select file
26447          * @param {Roo.bootstrap.DocumentManager} this
26448          */
26449         "beforeselectfile" : true,
26450         /**
26451          * @event process
26452          * Fire before process file
26453          * @param {Roo.bootstrap.DocumentManager} this
26454          * @param {Object} file
26455          */
26456         "process" : true
26457         
26458     });
26459 };
26460
26461 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26462     
26463     boxes : 0,
26464     inputName : '',
26465     thumbSize : 300,
26466     multiple : true,
26467     files : [],
26468     method : 'POST',
26469     url : '',
26470     paramName : 'imageUpload',
26471     fieldLabel : '',
26472     labelWidth : 4,
26473     labelAlign : 'left',
26474     editable : true,
26475     delegates : [],
26476     
26477     
26478     xhr : false, 
26479     
26480     getAutoCreate : function()
26481     {   
26482         var managerWidget = {
26483             tag : 'div',
26484             cls : 'roo-document-manager',
26485             cn : [
26486                 {
26487                     tag : 'input',
26488                     cls : 'roo-document-manager-selector',
26489                     type : 'file'
26490                 },
26491                 {
26492                     tag : 'div',
26493                     cls : 'roo-document-manager-uploader',
26494                     cn : [
26495                         {
26496                             tag : 'div',
26497                             cls : 'roo-document-manager-upload-btn',
26498                             html : '<i class="fa fa-plus"></i>'
26499                         }
26500                     ]
26501                     
26502                 }
26503             ]
26504         };
26505         
26506         var content = [
26507             {
26508                 tag : 'div',
26509                 cls : 'column col-md-12',
26510                 cn : managerWidget
26511             }
26512         ];
26513         
26514         if(this.fieldLabel.length){
26515             
26516             content = [
26517                 {
26518                     tag : 'div',
26519                     cls : 'column col-md-12',
26520                     html : this.fieldLabel
26521                 },
26522                 {
26523                     tag : 'div',
26524                     cls : 'column col-md-12',
26525                     cn : managerWidget
26526                 }
26527             ];
26528
26529             if(this.labelAlign == 'left'){
26530                 content = [
26531                     {
26532                         tag : 'div',
26533                         cls : 'column col-md-' + this.labelWidth,
26534                         html : this.fieldLabel
26535                     },
26536                     {
26537                         tag : 'div',
26538                         cls : 'column col-md-' + (12 - this.labelWidth),
26539                         cn : managerWidget
26540                     }
26541                 ];
26542                 
26543             }
26544         }
26545         
26546         var cfg = {
26547             tag : 'div',
26548             cls : 'row clearfix',
26549             cn : content
26550         };
26551         
26552         return cfg;
26553         
26554     },
26555     
26556     initEvents : function()
26557     {
26558         this.managerEl = this.el.select('.roo-document-manager', true).first();
26559         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26560         
26561         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26562         this.selectorEl.hide();
26563         
26564         if(this.multiple){
26565             this.selectorEl.attr('multiple', 'multiple');
26566         }
26567         
26568         this.selectorEl.on('change', this.onFileSelected, this);
26569         
26570         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26571         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26572         
26573         this.uploader.on('click', this.onUploaderClick, this);
26574         
26575         this.renderProgressDialog();
26576         
26577         var _this = this;
26578         
26579         window.addEventListener("resize", function() { _this.refresh(); } );
26580         
26581         this.fireEvent('initial', this);
26582     },
26583     
26584     renderProgressDialog : function()
26585     {
26586         var _this = this;
26587         
26588         this.progressDialog = new Roo.bootstrap.Modal({
26589             cls : 'roo-document-manager-progress-dialog',
26590             allow_close : false,
26591             title : '',
26592             buttons : [
26593                 {
26594                     name  :'cancel',
26595                     weight : 'danger',
26596                     html : 'Cancel'
26597                 }
26598             ], 
26599             listeners : { 
26600                 btnclick : function() {
26601                     _this.uploadCancel();
26602                     this.hide();
26603                 }
26604             }
26605         });
26606          
26607         this.progressDialog.render(Roo.get(document.body));
26608          
26609         this.progress = new Roo.bootstrap.Progress({
26610             cls : 'roo-document-manager-progress',
26611             active : true,
26612             striped : true
26613         });
26614         
26615         this.progress.render(this.progressDialog.getChildContainer());
26616         
26617         this.progressBar = new Roo.bootstrap.ProgressBar({
26618             cls : 'roo-document-manager-progress-bar',
26619             aria_valuenow : 0,
26620             aria_valuemin : 0,
26621             aria_valuemax : 12,
26622             panel : 'success'
26623         });
26624         
26625         this.progressBar.render(this.progress.getChildContainer());
26626     },
26627     
26628     onUploaderClick : function(e)
26629     {
26630         e.preventDefault();
26631      
26632         if(this.fireEvent('beforeselectfile', this) != false){
26633             this.selectorEl.dom.click();
26634         }
26635         
26636     },
26637     
26638     onFileSelected : function(e)
26639     {
26640         e.preventDefault();
26641         
26642         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26643             return;
26644         }
26645         
26646         Roo.each(this.selectorEl.dom.files, function(file){
26647             if(this.fireEvent('inspect', this, file) != false){
26648                 this.files.push(file);
26649             }
26650         }, this);
26651         
26652         this.queue();
26653         
26654     },
26655     
26656     queue : function()
26657     {
26658         this.selectorEl.dom.value = '';
26659         
26660         if(!this.files.length){
26661             return;
26662         }
26663         
26664         if(this.boxes > 0 && this.files.length > this.boxes){
26665             this.files = this.files.slice(0, this.boxes);
26666         }
26667         
26668         this.uploader.show();
26669         
26670         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26671             this.uploader.hide();
26672         }
26673         
26674         var _this = this;
26675         
26676         var files = [];
26677         
26678         var docs = [];
26679         
26680         Roo.each(this.files, function(file){
26681             
26682             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26683                 var f = this.renderPreview(file);
26684                 files.push(f);
26685                 return;
26686             }
26687             
26688             if(file.type.indexOf('image') != -1){
26689                 this.delegates.push(
26690                     (function(){
26691                         _this.process(file);
26692                     }).createDelegate(this)
26693                 );
26694         
26695                 return;
26696             }
26697             
26698             docs.push(
26699                 (function(){
26700                     _this.process(file);
26701                 }).createDelegate(this)
26702             );
26703             
26704         }, this);
26705         
26706         this.files = files;
26707         
26708         this.delegates = this.delegates.concat(docs);
26709         
26710         if(!this.delegates.length){
26711             this.refresh();
26712             return;
26713         }
26714         
26715         this.progressBar.aria_valuemax = this.delegates.length;
26716         
26717         this.arrange();
26718         
26719         return;
26720     },
26721     
26722     arrange : function()
26723     {
26724         if(!this.delegates.length){
26725             this.progressDialog.hide();
26726             this.refresh();
26727             return;
26728         }
26729         
26730         var delegate = this.delegates.shift();
26731         
26732         this.progressDialog.show();
26733         
26734         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26735         
26736         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26737         
26738         delegate();
26739     },
26740     
26741     refresh : function()
26742     {
26743         this.uploader.show();
26744         
26745         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26746             this.uploader.hide();
26747         }
26748         
26749         Roo.isTouch ? this.closable(false) : this.closable(true);
26750         
26751         this.fireEvent('refresh', this);
26752     },
26753     
26754     onRemove : function(e, el, o)
26755     {
26756         e.preventDefault();
26757         
26758         this.fireEvent('remove', this, o);
26759         
26760     },
26761     
26762     remove : function(o)
26763     {
26764         var files = [];
26765         
26766         Roo.each(this.files, function(file){
26767             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26768                 files.push(file);
26769                 return;
26770             }
26771
26772             o.target.remove();
26773
26774         }, this);
26775         
26776         this.files = files;
26777         
26778         this.refresh();
26779     },
26780     
26781     clear : function()
26782     {
26783         Roo.each(this.files, function(file){
26784             if(!file.target){
26785                 return;
26786             }
26787             
26788             file.target.remove();
26789
26790         }, this);
26791         
26792         this.files = [];
26793         
26794         this.refresh();
26795     },
26796     
26797     onClick : function(e, el, o)
26798     {
26799         e.preventDefault();
26800         
26801         this.fireEvent('click', this, o);
26802         
26803     },
26804     
26805     closable : function(closable)
26806     {
26807         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26808             
26809             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26810             
26811             if(closable){
26812                 el.show();
26813                 return;
26814             }
26815             
26816             el.hide();
26817             
26818         }, this);
26819     },
26820     
26821     xhrOnLoad : function(xhr)
26822     {
26823         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26824             el.remove();
26825         }, this);
26826         
26827         if (xhr.readyState !== 4) {
26828             this.arrange();
26829             this.fireEvent('exception', this, xhr);
26830             return;
26831         }
26832
26833         var response = Roo.decode(xhr.responseText);
26834         
26835         if(!response.success){
26836             this.arrange();
26837             this.fireEvent('exception', this, xhr);
26838             return;
26839         }
26840         
26841         var file = this.renderPreview(response.data);
26842         
26843         this.files.push(file);
26844         
26845         this.arrange();
26846         
26847     },
26848     
26849     xhrOnError : function(xhr)
26850     {
26851         Roo.log('xhr on error');
26852         
26853         var response = Roo.decode(xhr.responseText);
26854           
26855         Roo.log(response);
26856         
26857         this.arrange();
26858     },
26859     
26860     process : function(file)
26861     {
26862         if(this.fireEvent('process', this, file) !== false){
26863             if(this.editable && file.type.indexOf('image') != -1){
26864                 this.fireEvent('edit', this, file);
26865                 return;
26866             }
26867
26868             this.uploadStart(file, false);
26869
26870             return;
26871         }
26872         
26873     },
26874     
26875     uploadStart : function(file, crop)
26876     {
26877         this.xhr = new XMLHttpRequest();
26878         
26879         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26880             this.arrange();
26881             return;
26882         }
26883         
26884         file.xhr = this.xhr;
26885             
26886         this.managerEl.createChild({
26887             tag : 'div',
26888             cls : 'roo-document-manager-loading',
26889             cn : [
26890                 {
26891                     tag : 'div',
26892                     tooltip : file.name,
26893                     cls : 'roo-document-manager-thumb',
26894                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26895                 }
26896             ]
26897
26898         });
26899
26900         this.xhr.open(this.method, this.url, true);
26901         
26902         var headers = {
26903             "Accept": "application/json",
26904             "Cache-Control": "no-cache",
26905             "X-Requested-With": "XMLHttpRequest"
26906         };
26907         
26908         for (var headerName in headers) {
26909             var headerValue = headers[headerName];
26910             if (headerValue) {
26911                 this.xhr.setRequestHeader(headerName, headerValue);
26912             }
26913         }
26914         
26915         var _this = this;
26916         
26917         this.xhr.onload = function()
26918         {
26919             _this.xhrOnLoad(_this.xhr);
26920         }
26921         
26922         this.xhr.onerror = function()
26923         {
26924             _this.xhrOnError(_this.xhr);
26925         }
26926         
26927         var formData = new FormData();
26928
26929         formData.append('returnHTML', 'NO');
26930         
26931         if(crop){
26932             formData.append('crop', crop);
26933         }
26934         
26935         formData.append(this.paramName, file, file.name);
26936         
26937         if(this.fireEvent('prepare', this, formData) != false){
26938             this.xhr.send(formData);
26939         };
26940     },
26941     
26942     uploadCancel : function()
26943     {
26944         if (this.xhr) {
26945             this.xhr.abort();
26946         }
26947         
26948         
26949         this.delegates = [];
26950         
26951         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26952             el.remove();
26953         }, this);
26954         
26955         this.arrange();
26956     },
26957     
26958     renderPreview : function(file)
26959     {
26960         if(typeof(file.target) != 'undefined' && file.target){
26961             return file;
26962         }
26963         
26964         var previewEl = this.managerEl.createChild({
26965             tag : 'div',
26966             cls : 'roo-document-manager-preview',
26967             cn : [
26968                 {
26969                     tag : 'div',
26970                     tooltip : file.filename,
26971                     cls : 'roo-document-manager-thumb',
26972                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26973                 },
26974                 {
26975                     tag : 'button',
26976                     cls : 'close',
26977                     html : '<i class="fa fa-times-circle"></i>'
26978                 }
26979             ]
26980         });
26981
26982         var close = previewEl.select('button.close', true).first();
26983
26984         close.on('click', this.onRemove, this, file);
26985
26986         file.target = previewEl;
26987
26988         var image = previewEl.select('img', true).first();
26989         
26990         var _this = this;
26991         
26992         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26993         
26994         image.on('click', this.onClick, this, file);
26995         
26996         return file;
26997         
26998     },
26999     
27000     onPreviewLoad : function(file, image)
27001     {
27002         if(typeof(file.target) == 'undefined' || !file.target){
27003             return;
27004         }
27005         
27006         var width = image.dom.naturalWidth || image.dom.width;
27007         var height = image.dom.naturalHeight || image.dom.height;
27008         
27009         if(width > height){
27010             file.target.addClass('wide');
27011             return;
27012         }
27013         
27014         file.target.addClass('tall');
27015         return;
27016         
27017     },
27018     
27019     uploadFromSource : function(file, crop)
27020     {
27021         this.xhr = new XMLHttpRequest();
27022         
27023         this.managerEl.createChild({
27024             tag : 'div',
27025             cls : 'roo-document-manager-loading',
27026             cn : [
27027                 {
27028                     tag : 'div',
27029                     tooltip : file.name,
27030                     cls : 'roo-document-manager-thumb',
27031                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27032                 }
27033             ]
27034
27035         });
27036
27037         this.xhr.open(this.method, this.url, true);
27038         
27039         var headers = {
27040             "Accept": "application/json",
27041             "Cache-Control": "no-cache",
27042             "X-Requested-With": "XMLHttpRequest"
27043         };
27044         
27045         for (var headerName in headers) {
27046             var headerValue = headers[headerName];
27047             if (headerValue) {
27048                 this.xhr.setRequestHeader(headerName, headerValue);
27049             }
27050         }
27051         
27052         var _this = this;
27053         
27054         this.xhr.onload = function()
27055         {
27056             _this.xhrOnLoad(_this.xhr);
27057         }
27058         
27059         this.xhr.onerror = function()
27060         {
27061             _this.xhrOnError(_this.xhr);
27062         }
27063         
27064         var formData = new FormData();
27065
27066         formData.append('returnHTML', 'NO');
27067         
27068         formData.append('crop', crop);
27069         
27070         if(typeof(file.filename) != 'undefined'){
27071             formData.append('filename', file.filename);
27072         }
27073         
27074         if(typeof(file.mimetype) != 'undefined'){
27075             formData.append('mimetype', file.mimetype);
27076         }
27077         
27078         if(this.fireEvent('prepare', this, formData) != false){
27079             this.xhr.send(formData);
27080         };
27081     }
27082 });
27083
27084 /*
27085 * Licence: LGPL
27086 */
27087
27088 /**
27089  * @class Roo.bootstrap.DocumentViewer
27090  * @extends Roo.bootstrap.Component
27091  * Bootstrap DocumentViewer class
27092  * 
27093  * @constructor
27094  * Create a new DocumentViewer
27095  * @param {Object} config The config object
27096  */
27097
27098 Roo.bootstrap.DocumentViewer = function(config){
27099     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27100     
27101     this.addEvents({
27102         /**
27103          * @event initial
27104          * Fire after initEvent
27105          * @param {Roo.bootstrap.DocumentViewer} this
27106          */
27107         "initial" : true,
27108         /**
27109          * @event click
27110          * Fire after click
27111          * @param {Roo.bootstrap.DocumentViewer} this
27112          */
27113         "click" : true,
27114         /**
27115          * @event trash
27116          * Fire after trash button
27117          * @param {Roo.bootstrap.DocumentViewer} this
27118          */
27119         "trash" : true
27120         
27121     });
27122 };
27123
27124 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27125     
27126     getAutoCreate : function()
27127     {
27128         var cfg = {
27129             tag : 'div',
27130             cls : 'roo-document-viewer',
27131             cn : [
27132                 {
27133                     tag : 'div',
27134                     cls : 'roo-document-viewer-body',
27135                     cn : [
27136                         {
27137                             tag : 'div',
27138                             cls : 'roo-document-viewer-thumb',
27139                             cn : [
27140                                 {
27141                                     tag : 'img',
27142                                     cls : 'roo-document-viewer-image'
27143                                 }
27144                             ]
27145                         }
27146                     ]
27147                 },
27148                 {
27149                     tag : 'div',
27150                     cls : 'roo-document-viewer-footer',
27151                     cn : {
27152                         tag : 'div',
27153                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27154                         cn : [
27155                             {
27156                                 tag : 'div',
27157                                 cls : 'btn-group',
27158                                 cn : [
27159                                     {
27160                                         tag : 'button',
27161                                         cls : 'btn btn-default roo-document-viewer-trash',
27162                                         html : '<i class="fa fa-trash"></i>'
27163                                     }
27164                                 ]
27165                             }
27166                         ]
27167                     }
27168                 }
27169             ]
27170         };
27171         
27172         return cfg;
27173     },
27174     
27175     initEvents : function()
27176     {
27177         
27178         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27179         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27180         
27181         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27182         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27183         
27184         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27185         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27186         
27187         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27188         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27189         
27190         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27191         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27192         
27193         this.bodyEl.on('click', this.onClick, this);
27194         
27195         this.trashBtn.on('click', this.onTrash, this);
27196         
27197     },
27198     
27199     initial : function()
27200     {
27201 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27202         
27203         
27204         this.fireEvent('initial', this);
27205         
27206     },
27207     
27208     onClick : function(e)
27209     {
27210         e.preventDefault();
27211         
27212         this.fireEvent('click', this);
27213     },
27214     
27215     onTrash : function(e)
27216     {
27217         e.preventDefault();
27218         
27219         this.fireEvent('trash', this);
27220     }
27221     
27222 });
27223 /*
27224  * - LGPL
27225  *
27226  * nav progress bar
27227  * 
27228  */
27229
27230 /**
27231  * @class Roo.bootstrap.NavProgressBar
27232  * @extends Roo.bootstrap.Component
27233  * Bootstrap NavProgressBar class
27234  * 
27235  * @constructor
27236  * Create a new nav progress bar
27237  * @param {Object} config The config object
27238  */
27239
27240 Roo.bootstrap.NavProgressBar = function(config){
27241     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27242
27243     this.bullets = this.bullets || [];
27244    
27245 //    Roo.bootstrap.NavProgressBar.register(this);
27246      this.addEvents({
27247         /**
27248              * @event changed
27249              * Fires when the active item changes
27250              * @param {Roo.bootstrap.NavProgressBar} this
27251              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27252              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27253          */
27254         'changed': true
27255      });
27256     
27257 };
27258
27259 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27260     
27261     bullets : [],
27262     barItems : [],
27263     
27264     getAutoCreate : function()
27265     {
27266         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27267         
27268         cfg = {
27269             tag : 'div',
27270             cls : 'roo-navigation-bar-group',
27271             cn : [
27272                 {
27273                     tag : 'div',
27274                     cls : 'roo-navigation-top-bar'
27275                 },
27276                 {
27277                     tag : 'div',
27278                     cls : 'roo-navigation-bullets-bar',
27279                     cn : [
27280                         {
27281                             tag : 'ul',
27282                             cls : 'roo-navigation-bar'
27283                         }
27284                     ]
27285                 },
27286                 
27287                 {
27288                     tag : 'div',
27289                     cls : 'roo-navigation-bottom-bar'
27290                 }
27291             ]
27292             
27293         };
27294         
27295         return cfg;
27296         
27297     },
27298     
27299     initEvents: function() 
27300     {
27301         
27302     },
27303     
27304     onRender : function(ct, position) 
27305     {
27306         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27307         
27308         if(this.bullets.length){
27309             Roo.each(this.bullets, function(b){
27310                this.addItem(b);
27311             }, this);
27312         }
27313         
27314         this.format();
27315         
27316     },
27317     
27318     addItem : function(cfg)
27319     {
27320         var item = new Roo.bootstrap.NavProgressItem(cfg);
27321         
27322         item.parentId = this.id;
27323         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27324         
27325         if(cfg.html){
27326             var top = new Roo.bootstrap.Element({
27327                 tag : 'div',
27328                 cls : 'roo-navigation-bar-text'
27329             });
27330             
27331             var bottom = new Roo.bootstrap.Element({
27332                 tag : 'div',
27333                 cls : 'roo-navigation-bar-text'
27334             });
27335             
27336             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27337             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27338             
27339             var topText = new Roo.bootstrap.Element({
27340                 tag : 'span',
27341                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27342             });
27343             
27344             var bottomText = new Roo.bootstrap.Element({
27345                 tag : 'span',
27346                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27347             });
27348             
27349             topText.onRender(top.el, null);
27350             bottomText.onRender(bottom.el, null);
27351             
27352             item.topEl = top;
27353             item.bottomEl = bottom;
27354         }
27355         
27356         this.barItems.push(item);
27357         
27358         return item;
27359     },
27360     
27361     getActive : function()
27362     {
27363         var active = false;
27364         
27365         Roo.each(this.barItems, function(v){
27366             
27367             if (!v.isActive()) {
27368                 return;
27369             }
27370             
27371             active = v;
27372             return false;
27373             
27374         });
27375         
27376         return active;
27377     },
27378     
27379     setActiveItem : function(item)
27380     {
27381         var prev = false;
27382         
27383         Roo.each(this.barItems, function(v){
27384             if (v.rid == item.rid) {
27385                 return ;
27386             }
27387             
27388             if (v.isActive()) {
27389                 v.setActive(false);
27390                 prev = v;
27391             }
27392         });
27393
27394         item.setActive(true);
27395         
27396         this.fireEvent('changed', this, item, prev);
27397     },
27398     
27399     getBarItem: function(rid)
27400     {
27401         var ret = false;
27402         
27403         Roo.each(this.barItems, function(e) {
27404             if (e.rid != rid) {
27405                 return;
27406             }
27407             
27408             ret =  e;
27409             return false;
27410         });
27411         
27412         return ret;
27413     },
27414     
27415     indexOfItem : function(item)
27416     {
27417         var index = false;
27418         
27419         Roo.each(this.barItems, function(v, i){
27420             
27421             if (v.rid != item.rid) {
27422                 return;
27423             }
27424             
27425             index = i;
27426             return false
27427         });
27428         
27429         return index;
27430     },
27431     
27432     setActiveNext : function()
27433     {
27434         var i = this.indexOfItem(this.getActive());
27435         
27436         if (i > this.barItems.length) {
27437             return;
27438         }
27439         
27440         this.setActiveItem(this.barItems[i+1]);
27441     },
27442     
27443     setActivePrev : function()
27444     {
27445         var i = this.indexOfItem(this.getActive());
27446         
27447         if (i  < 1) {
27448             return;
27449         }
27450         
27451         this.setActiveItem(this.barItems[i-1]);
27452     },
27453     
27454     format : function()
27455     {
27456         if(!this.barItems.length){
27457             return;
27458         }
27459      
27460         var width = 100 / this.barItems.length;
27461         
27462         Roo.each(this.barItems, function(i){
27463             i.el.setStyle('width', width + '%');
27464             i.topEl.el.setStyle('width', width + '%');
27465             i.bottomEl.el.setStyle('width', width + '%');
27466         }, this);
27467         
27468     }
27469     
27470 });
27471 /*
27472  * - LGPL
27473  *
27474  * Nav Progress Item
27475  * 
27476  */
27477
27478 /**
27479  * @class Roo.bootstrap.NavProgressItem
27480  * @extends Roo.bootstrap.Component
27481  * Bootstrap NavProgressItem class
27482  * @cfg {String} rid the reference id
27483  * @cfg {Boolean} active (true|false) Is item active default false
27484  * @cfg {Boolean} disabled (true|false) Is item active default false
27485  * @cfg {String} html
27486  * @cfg {String} position (top|bottom) text position default bottom
27487  * @cfg {String} icon show icon instead of number
27488  * 
27489  * @constructor
27490  * Create a new NavProgressItem
27491  * @param {Object} config The config object
27492  */
27493 Roo.bootstrap.NavProgressItem = function(config){
27494     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27495     this.addEvents({
27496         // raw events
27497         /**
27498          * @event click
27499          * The raw click event for the entire grid.
27500          * @param {Roo.bootstrap.NavProgressItem} this
27501          * @param {Roo.EventObject} e
27502          */
27503         "click" : true
27504     });
27505    
27506 };
27507
27508 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27509     
27510     rid : '',
27511     active : false,
27512     disabled : false,
27513     html : '',
27514     position : 'bottom',
27515     icon : false,
27516     
27517     getAutoCreate : function()
27518     {
27519         var iconCls = 'roo-navigation-bar-item-icon';
27520         
27521         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27522         
27523         var cfg = {
27524             tag: 'li',
27525             cls: 'roo-navigation-bar-item',
27526             cn : [
27527                 {
27528                     tag : 'i',
27529                     cls : iconCls
27530                 }
27531             ]
27532         };
27533         
27534         if(this.active){
27535             cfg.cls += ' active';
27536         }
27537         if(this.disabled){
27538             cfg.cls += ' disabled';
27539         }
27540         
27541         return cfg;
27542     },
27543     
27544     disable : function()
27545     {
27546         this.setDisabled(true);
27547     },
27548     
27549     enable : function()
27550     {
27551         this.setDisabled(false);
27552     },
27553     
27554     initEvents: function() 
27555     {
27556         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27557         
27558         this.iconEl.on('click', this.onClick, this);
27559     },
27560     
27561     onClick : function(e)
27562     {
27563         e.preventDefault();
27564         
27565         if(this.disabled){
27566             return;
27567         }
27568         
27569         if(this.fireEvent('click', this, e) === false){
27570             return;
27571         };
27572         
27573         this.parent().setActiveItem(this);
27574     },
27575     
27576     isActive: function () 
27577     {
27578         return this.active;
27579     },
27580     
27581     setActive : function(state)
27582     {
27583         if(this.active == state){
27584             return;
27585         }
27586         
27587         this.active = state;
27588         
27589         if (state) {
27590             this.el.addClass('active');
27591             return;
27592         }
27593         
27594         this.el.removeClass('active');
27595         
27596         return;
27597     },
27598     
27599     setDisabled : function(state)
27600     {
27601         if(this.disabled == state){
27602             return;
27603         }
27604         
27605         this.disabled = state;
27606         
27607         if (state) {
27608             this.el.addClass('disabled');
27609             return;
27610         }
27611         
27612         this.el.removeClass('disabled');
27613     },
27614     
27615     tooltipEl : function()
27616     {
27617         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27618     }
27619 });
27620  
27621
27622  /*
27623  * - LGPL
27624  *
27625  * FieldLabel
27626  * 
27627  */
27628
27629 /**
27630  * @class Roo.bootstrap.FieldLabel
27631  * @extends Roo.bootstrap.Component
27632  * Bootstrap FieldLabel class
27633  * @cfg {String} html contents of the element
27634  * @cfg {String} tag tag of the element default label
27635  * @cfg {String} cls class of the element
27636  * @cfg {String} target label target 
27637  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27638  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27639  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27640  * @cfg {String} iconTooltip default "This field is required"
27641  * 
27642  * @constructor
27643  * Create a new FieldLabel
27644  * @param {Object} config The config object
27645  */
27646
27647 Roo.bootstrap.FieldLabel = function(config){
27648     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27649     
27650     this.addEvents({
27651             /**
27652              * @event invalid
27653              * Fires after the field has been marked as invalid.
27654              * @param {Roo.form.FieldLabel} this
27655              * @param {String} msg The validation message
27656              */
27657             invalid : true,
27658             /**
27659              * @event valid
27660              * Fires after the field has been validated with no errors.
27661              * @param {Roo.form.FieldLabel} this
27662              */
27663             valid : true
27664         });
27665 };
27666
27667 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27668     
27669     tag: 'label',
27670     cls: '',
27671     html: '',
27672     target: '',
27673     allowBlank : true,
27674     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27675     validClass : 'text-success fa fa-lg fa-check',
27676     iconTooltip : 'This field is required',
27677     
27678     getAutoCreate : function(){
27679         
27680         var cfg = {
27681             tag : this.tag,
27682             cls : 'roo-bootstrap-field-label ' + this.cls,
27683             for : this.target,
27684             cn : [
27685                 {
27686                     tag : 'i',
27687                     cls : '',
27688                     tooltip : this.iconTooltip
27689                 },
27690                 {
27691                     tag : 'span',
27692                     html : this.html
27693                 }
27694             ] 
27695         };
27696         
27697         return cfg;
27698     },
27699     
27700     initEvents: function() 
27701     {
27702         Roo.bootstrap.Element.superclass.initEvents.call(this);
27703         
27704         this.iconEl = this.el.select('i', true).first();
27705         
27706         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27707         
27708         Roo.bootstrap.FieldLabel.register(this);
27709     },
27710     
27711     /**
27712      * Mark this field as valid
27713      */
27714     markValid : function()
27715     {
27716         this.iconEl.show();
27717         
27718         this.iconEl.removeClass(this.invalidClass);
27719         
27720         this.iconEl.addClass(this.validClass);
27721         
27722         this.fireEvent('valid', this);
27723     },
27724     
27725     /**
27726      * Mark this field as invalid
27727      * @param {String} msg The validation message
27728      */
27729     markInvalid : function(msg)
27730     {
27731         this.iconEl.show();
27732         
27733         this.iconEl.removeClass(this.validClass);
27734         
27735         this.iconEl.addClass(this.invalidClass);
27736         
27737         this.fireEvent('invalid', this, msg);
27738     }
27739     
27740    
27741 });
27742
27743 Roo.apply(Roo.bootstrap.FieldLabel, {
27744     
27745     groups: {},
27746     
27747      /**
27748     * register a FieldLabel Group
27749     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27750     */
27751     register : function(label)
27752     {
27753         if(this.groups.hasOwnProperty(label.target)){
27754             return;
27755         }
27756      
27757         this.groups[label.target] = label;
27758         
27759     },
27760     /**
27761     * fetch a FieldLabel Group based on the target
27762     * @param {string} target
27763     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27764     */
27765     get: function(target) {
27766         if (typeof(this.groups[target]) == 'undefined') {
27767             return false;
27768         }
27769         
27770         return this.groups[target] ;
27771     }
27772 });
27773
27774  
27775
27776  /*
27777  * - LGPL
27778  *
27779  * page DateSplitField.
27780  * 
27781  */
27782
27783
27784 /**
27785  * @class Roo.bootstrap.DateSplitField
27786  * @extends Roo.bootstrap.Component
27787  * Bootstrap DateSplitField class
27788  * @cfg {string} fieldLabel - the label associated
27789  * @cfg {Number} labelWidth set the width of label (0-12)
27790  * @cfg {String} labelAlign (top|left)
27791  * @cfg {Boolean} dayAllowBlank (true|false) default false
27792  * @cfg {Boolean} monthAllowBlank (true|false) default false
27793  * @cfg {Boolean} yearAllowBlank (true|false) default false
27794  * @cfg {string} dayPlaceholder 
27795  * @cfg {string} monthPlaceholder
27796  * @cfg {string} yearPlaceholder
27797  * @cfg {string} dayFormat default 'd'
27798  * @cfg {string} monthFormat default 'm'
27799  * @cfg {string} yearFormat default 'Y'
27800
27801  *     
27802  * @constructor
27803  * Create a new DateSplitField
27804  * @param {Object} config The config object
27805  */
27806
27807 Roo.bootstrap.DateSplitField = function(config){
27808     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27809     
27810     this.addEvents({
27811         // raw events
27812          /**
27813          * @event years
27814          * getting the data of years
27815          * @param {Roo.bootstrap.DateSplitField} this
27816          * @param {Object} years
27817          */
27818         "years" : true,
27819         /**
27820          * @event days
27821          * getting the data of days
27822          * @param {Roo.bootstrap.DateSplitField} this
27823          * @param {Object} days
27824          */
27825         "days" : true,
27826         /**
27827          * @event invalid
27828          * Fires after the field has been marked as invalid.
27829          * @param {Roo.form.Field} this
27830          * @param {String} msg The validation message
27831          */
27832         invalid : true,
27833        /**
27834          * @event valid
27835          * Fires after the field has been validated with no errors.
27836          * @param {Roo.form.Field} this
27837          */
27838         valid : true
27839     });
27840 };
27841
27842 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27843     
27844     fieldLabel : '',
27845     labelAlign : 'top',
27846     labelWidth : 3,
27847     dayAllowBlank : false,
27848     monthAllowBlank : false,
27849     yearAllowBlank : false,
27850     dayPlaceholder : '',
27851     monthPlaceholder : '',
27852     yearPlaceholder : '',
27853     dayFormat : 'd',
27854     monthFormat : 'm',
27855     yearFormat : 'Y',
27856     isFormField : true,
27857     
27858     getAutoCreate : function()
27859     {
27860         var cfg = {
27861             tag : 'div',
27862             cls : 'row roo-date-split-field-group',
27863             cn : [
27864                 {
27865                     tag : 'input',
27866                     type : 'hidden',
27867                     cls : 'form-hidden-field roo-date-split-field-group-value',
27868                     name : this.name
27869                 }
27870             ]
27871         };
27872         
27873         if(this.fieldLabel){
27874             cfg.cn.push({
27875                 tag : 'div',
27876                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27877                 cn : [
27878                     {
27879                         tag : 'label',
27880                         html : this.fieldLabel
27881                     }
27882                 ]
27883             });
27884         }
27885         
27886         Roo.each(['day', 'month', 'year'], function(t){
27887             cfg.cn.push({
27888                 tag : 'div',
27889                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27890             });
27891         }, this);
27892         
27893         return cfg;
27894     },
27895     
27896     inputEl: function ()
27897     {
27898         return this.el.select('.roo-date-split-field-group-value', true).first();
27899     },
27900     
27901     onRender : function(ct, position) 
27902     {
27903         var _this = this;
27904         
27905         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27906         
27907         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27908         
27909         this.dayField = new Roo.bootstrap.ComboBox({
27910             allowBlank : this.dayAllowBlank,
27911             alwaysQuery : true,
27912             displayField : 'value',
27913             editable : false,
27914             fieldLabel : '',
27915             forceSelection : true,
27916             mode : 'local',
27917             placeholder : this.dayPlaceholder,
27918             selectOnFocus : true,
27919             tpl : '<div class="select2-result"><b>{value}</b></div>',
27920             triggerAction : 'all',
27921             typeAhead : true,
27922             valueField : 'value',
27923             store : new Roo.data.SimpleStore({
27924                 data : (function() {    
27925                     var days = [];
27926                     _this.fireEvent('days', _this, days);
27927                     return days;
27928                 })(),
27929                 fields : [ 'value' ]
27930             }),
27931             listeners : {
27932                 select : function (_self, record, index)
27933                 {
27934                     _this.setValue(_this.getValue());
27935                 }
27936             }
27937         });
27938
27939         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27940         
27941         this.monthField = new Roo.bootstrap.MonthField({
27942             after : '<i class=\"fa fa-calendar\"></i>',
27943             allowBlank : this.monthAllowBlank,
27944             placeholder : this.monthPlaceholder,
27945             readOnly : true,
27946             listeners : {
27947                 render : function (_self)
27948                 {
27949                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27950                         e.preventDefault();
27951                         _self.focus();
27952                     });
27953                 },
27954                 select : function (_self, oldvalue, newvalue)
27955                 {
27956                     _this.setValue(_this.getValue());
27957                 }
27958             }
27959         });
27960         
27961         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27962         
27963         this.yearField = new Roo.bootstrap.ComboBox({
27964             allowBlank : this.yearAllowBlank,
27965             alwaysQuery : true,
27966             displayField : 'value',
27967             editable : false,
27968             fieldLabel : '',
27969             forceSelection : true,
27970             mode : 'local',
27971             placeholder : this.yearPlaceholder,
27972             selectOnFocus : true,
27973             tpl : '<div class="select2-result"><b>{value}</b></div>',
27974             triggerAction : 'all',
27975             typeAhead : true,
27976             valueField : 'value',
27977             store : new Roo.data.SimpleStore({
27978                 data : (function() {
27979                     var years = [];
27980                     _this.fireEvent('years', _this, years);
27981                     return years;
27982                 })(),
27983                 fields : [ 'value' ]
27984             }),
27985             listeners : {
27986                 select : function (_self, record, index)
27987                 {
27988                     _this.setValue(_this.getValue());
27989                 }
27990             }
27991         });
27992
27993         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27994     },
27995     
27996     setValue : function(v, format)
27997     {
27998         this.inputEl.dom.value = v;
27999         
28000         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28001         
28002         var d = Date.parseDate(v, f);
28003         
28004         if(!d){
28005             this.validate();
28006             return;
28007         }
28008         
28009         this.setDay(d.format(this.dayFormat));
28010         this.setMonth(d.format(this.monthFormat));
28011         this.setYear(d.format(this.yearFormat));
28012         
28013         this.validate();
28014         
28015         return;
28016     },
28017     
28018     setDay : function(v)
28019     {
28020         this.dayField.setValue(v);
28021         this.inputEl.dom.value = this.getValue();
28022         this.validate();
28023         return;
28024     },
28025     
28026     setMonth : function(v)
28027     {
28028         this.monthField.setValue(v, true);
28029         this.inputEl.dom.value = this.getValue();
28030         this.validate();
28031         return;
28032     },
28033     
28034     setYear : function(v)
28035     {
28036         this.yearField.setValue(v);
28037         this.inputEl.dom.value = this.getValue();
28038         this.validate();
28039         return;
28040     },
28041     
28042     getDay : function()
28043     {
28044         return this.dayField.getValue();
28045     },
28046     
28047     getMonth : function()
28048     {
28049         return this.monthField.getValue();
28050     },
28051     
28052     getYear : function()
28053     {
28054         return this.yearField.getValue();
28055     },
28056     
28057     getValue : function()
28058     {
28059         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28060         
28061         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28062         
28063         return date;
28064     },
28065     
28066     reset : function()
28067     {
28068         this.setDay('');
28069         this.setMonth('');
28070         this.setYear('');
28071         this.inputEl.dom.value = '';
28072         this.validate();
28073         return;
28074     },
28075     
28076     validate : function()
28077     {
28078         var d = this.dayField.validate();
28079         var m = this.monthField.validate();
28080         var y = this.yearField.validate();
28081         
28082         var valid = true;
28083         
28084         if(
28085                 (!this.dayAllowBlank && !d) ||
28086                 (!this.monthAllowBlank && !m) ||
28087                 (!this.yearAllowBlank && !y)
28088         ){
28089             valid = false;
28090         }
28091         
28092         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28093             return valid;
28094         }
28095         
28096         if(valid){
28097             this.markValid();
28098             return valid;
28099         }
28100         
28101         this.markInvalid();
28102         
28103         return valid;
28104     },
28105     
28106     markValid : function()
28107     {
28108         
28109         var label = this.el.select('label', true).first();
28110         var icon = this.el.select('i.fa-star', true).first();
28111
28112         if(label && icon){
28113             icon.remove();
28114         }
28115         
28116         this.fireEvent('valid', this);
28117     },
28118     
28119      /**
28120      * Mark this field as invalid
28121      * @param {String} msg The validation message
28122      */
28123     markInvalid : function(msg)
28124     {
28125         
28126         var label = this.el.select('label', true).first();
28127         var icon = this.el.select('i.fa-star', true).first();
28128
28129         if(label && !icon){
28130             this.el.select('.roo-date-split-field-label', true).createChild({
28131                 tag : 'i',
28132                 cls : 'text-danger fa fa-lg fa-star',
28133                 tooltip : 'This field is required',
28134                 style : 'margin-right:5px;'
28135             }, label, true);
28136         }
28137         
28138         this.fireEvent('invalid', this, msg);
28139     },
28140     
28141     clearInvalid : function()
28142     {
28143         var label = this.el.select('label', true).first();
28144         var icon = this.el.select('i.fa-star', true).first();
28145
28146         if(label && icon){
28147             icon.remove();
28148         }
28149         
28150         this.fireEvent('valid', this);
28151     },
28152     
28153     getName: function()
28154     {
28155         return this.name;
28156     }
28157     
28158 });
28159
28160