ux/Showdown.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         this.initEvents();
140         
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165            
166         cn.parentType = this.xtype; //??
167         cn.parentId = this.id;
168         
169         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
170         if (typeof(cn.container_method) == 'string') {
171             cntr = cn.container_method;
172         }
173         
174         
175         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
176         
177         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
178         
179         var build_from_html =  Roo.XComponent.build_from_html;
180           
181         var is_body  = (tree.xtype == 'Body') ;
182           
183         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184           
185         var self_cntr_el = Roo.get(this[cntr](false));
186         
187         // do not try and build conditional elements 
188         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
189             return false;
190         }
191         
192         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
193             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
194                 return this.addxtypeChild(tree,cntr, is_body);
195             }
196             
197             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
198                 
199             if(echild){
200                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
201             }
202             
203             Roo.log('skipping render');
204             return cn;
205             
206         }
207         
208         var ret = false;
209         if (!build_from_html) {
210             return false;
211         }
212         
213         // this i think handles overlaying multiple children of the same type
214         // with the sam eelement.. - which might be buggy..
215         while (true) {
216             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
217             
218             if (!echild) {
219                 break;
220             }
221             
222             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
223                 break;
224             }
225             
226             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
227         }
228         return ret;
229     },
230     
231     addxtypeChild : function (tree, cntr, is_body)
232     {
233         Roo.debug && Roo.log('addxtypeChild:' + cntr);
234         var cn = this;
235         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
236         
237         
238         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
239                     (typeof(tree['flexy:foreach']) != 'undefined');
240           
241         
242         
243          skip_children = false;
244         // render the element if it's not BODY.
245         if (!is_body) {
246            
247             cn = Roo.factory(tree);
248            
249             cn.parentType = this.xtype; //??
250             cn.parentId = this.id;
251             
252             var build_from_html =  Roo.XComponent.build_from_html;
253             
254             
255             // does the container contain child eleemnts with 'xtype' attributes.
256             // that match this xtype..
257             // note - when we render we create these as well..
258             // so we should check to see if body has xtype set.
259             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260                
261                 var self_cntr_el = Roo.get(this[cntr](false));
262                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263                 if (echild) { 
264                     //Roo.log(Roo.XComponent.build_from_html);
265                     //Roo.log("got echild:");
266                     //Roo.log(echild);
267                 }
268                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
269                 // and are not displayed -this causes this to use up the wrong element when matching.
270                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
271                 
272                 
273                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
274                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
275                   
276                   
277                   
278                     cn.el = echild;
279                   //  Roo.log("GOT");
280                     //echild.dom.removeAttribute('xtype');
281                 } else {
282                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
283                     Roo.debug && Roo.log(self_cntr_el);
284                     Roo.debug && Roo.log(echild);
285                     Roo.debug && Roo.log(cn);
286                 }
287             }
288            
289             
290            
291             // if object has flexy:if - then it may or may not be rendered.
292             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
293                 // skip a flexy if element.
294                 Roo.debug && Roo.log('skipping render');
295                 Roo.debug && Roo.log(tree);
296                 if (!cn.el) {
297                     Roo.debug && Roo.log('skipping all children');
298                     skip_children = true;
299                 }
300                 
301              } else {
302                  
303                 // actually if flexy:foreach is found, we really want to create 
304                 // multiple copies here...
305                 //Roo.log('render');
306                 //Roo.log(this[cntr]());
307                 cn.render(this[cntr](true));
308              }
309             // then add the element..
310         }
311         
312         
313         // handle the kids..
314         
315         var nitems = [];
316         /*
317         if (typeof (tree.menu) != 'undefined') {
318             tree.menu.parentType = cn.xtype;
319             tree.menu.triggerEl = cn.el;
320             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
321             
322         }
323         */
324         if (!tree.items || !tree.items.length) {
325             cn.items = nitems;
326             return cn;
327         }
328         var items = tree.items;
329         delete tree.items;
330         
331         //Roo.log(items.length);
332             // add the items..
333         if (!skip_children) {    
334             for(var i =0;i < items.length;i++) {
335                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
336             }
337         }
338         
339         cn.items = nitems;
340         
341         this.fireEvent('childrenrendered', this);
342         
343         return cn;
344     },
345     /**
346      * Show a component - removes 'hidden' class
347      */
348     show : function()
349     {
350         if (this.el) {
351             this.el.removeClass('hidden');
352         }
353     },
354     /**
355      * Hide a component - adds 'hidden' class
356      */
357     hide: function()
358     {
359         if (this.el && !this.el.hasClass('hidden')) {
360             this.el.addClass('hidden');
361         }
362         
363     }
364 });
365
366  /*
367  * - LGPL
368  *
369  * Body
370  * 
371  */
372
373 /**
374  * @class Roo.bootstrap.Body
375  * @extends Roo.bootstrap.Component
376  * Bootstrap Body class
377  * 
378  * @constructor
379  * Create a new body
380  * @param {Object} config The config object
381  */
382
383 Roo.bootstrap.Body = function(config){
384     Roo.bootstrap.Body.superclass.constructor.call(this, config);
385     this.el = Roo.get(document.body);
386     if (this.cls && this.cls.length) {
387         Roo.get(document.body).addClass(this.cls);
388     }
389 };
390
391 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
392     
393     is_body : true,// just to make sure it's constructed?
394     
395         autoCreate : {
396         cls: 'container'
397     },
398     onRender : function(ct, position)
399     {
400        /* Roo.log("Roo.bootstrap.Body - onRender");
401         if (this.cls && this.cls.length) {
402             Roo.get(document.body).addClass(this.cls);
403         }
404         // style??? xttr???
405         */
406     }
407     
408     
409  
410    
411 });
412
413  /*
414  * - LGPL
415  *
416  * button group
417  * 
418  */
419
420
421 /**
422  * @class Roo.bootstrap.ButtonGroup
423  * @extends Roo.bootstrap.Component
424  * Bootstrap ButtonGroup class
425  * @cfg {String} size lg | sm | xs (default empty normal)
426  * @cfg {String} align vertical | justified  (default none)
427  * @cfg {String} direction up | down (default down)
428  * @cfg {Boolean} toolbar false | true
429  * @cfg {Boolean} btn true | false
430  * 
431  * 
432  * @constructor
433  * Create a new Input
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.ButtonGroup = function(config){
438     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
439 };
440
441 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
442     
443     size: '',
444     align: '',
445     direction: '',
446     toolbar: false,
447     btn: true,
448
449     getAutoCreate : function(){
450         var cfg = {
451             cls: 'btn-group',
452             html : null
453         };
454         
455         cfg.html = this.html || cfg.html;
456         
457         if (this.toolbar) {
458             cfg = {
459                 cls: 'btn-toolbar',
460                 html: null
461             };
462             
463             return cfg;
464         }
465         
466         if (['vertical','justified'].indexOf(this.align)!==-1) {
467             cfg.cls = 'btn-group-' + this.align;
468             
469             if (this.align == 'justified') {
470                 console.log(this.items);
471             }
472         }
473         
474         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
475             cfg.cls += ' btn-group-' + this.size;
476         }
477         
478         if (this.direction == 'up') {
479             cfg.cls += ' dropup' ;
480         }
481         
482         return cfg;
483     }
484    
485 });
486
487  /*
488  * - LGPL
489  *
490  * button
491  * 
492  */
493
494 /**
495  * @class Roo.bootstrap.Button
496  * @extends Roo.bootstrap.Component
497  * Bootstrap Button class
498  * @cfg {String} html The button content
499  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
500  * @cfg {String} size ( lg | sm | xs)
501  * @cfg {String} tag ( a | input | submit)
502  * @cfg {String} href empty or href
503  * @cfg {Boolean} disabled default false;
504  * @cfg {Boolean} isClose default false;
505  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
506  * @cfg {String} badge text for badge
507  * @cfg {String} theme default 
508  * @cfg {Boolean} inverse 
509  * @cfg {Boolean} toggle 
510  * @cfg {String} ontext text for on toggle state
511  * @cfg {String} offtext text for off toggle state
512  * @cfg {Boolean} defaulton 
513  * @cfg {Boolean} preventDefault  default true
514  * @cfg {Boolean} removeClass remove the standard class..
515  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
516  * 
517  * @constructor
518  * Create a new button
519  * @param {Object} config The config object
520  */
521
522
523 Roo.bootstrap.Button = function(config){
524     Roo.bootstrap.Button.superclass.constructor.call(this, config);
525     this.addEvents({
526         // raw events
527         /**
528          * @event click
529          * When a butotn is pressed
530          * @param {Roo.bootstrap.Button} this
531          * @param {Roo.EventObject} e
532          */
533         "click" : true,
534          /**
535          * @event toggle
536          * After the button has been toggles
537          * @param {Roo.EventObject} e
538          * @param {boolean} pressed (also available as button.pressed)
539          */
540         "toggle" : true
541     });
542 };
543
544 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
545     html: false,
546     active: false,
547     weight: '',
548     size: '',
549     tag: 'button',
550     href: '',
551     disabled: false,
552     isClose: false,
553     glyphicon: '',
554     badge: '',
555     theme: 'default',
556     inverse: false,
557     
558     toggle: false,
559     ontext: 'ON',
560     offtext: 'OFF',
561     defaulton: true,
562     preventDefault: true,
563     removeClass: false,
564     name: false,
565     target: false,
566     
567     
568     pressed : null,
569      
570     
571     getAutoCreate : function(){
572         
573         var cfg = {
574             tag : 'button',
575             cls : 'roo-button',
576             html: ''
577         };
578         
579         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
580             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
581             this.tag = 'button';
582         } else {
583             cfg.tag = this.tag;
584         }
585         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
586         
587         if (this.toggle == true) {
588             cfg={
589                 tag: 'div',
590                 cls: 'slider-frame roo-button',
591                 cn: [
592                     {
593                         tag: 'span',
594                         'data-on-text':'ON',
595                         'data-off-text':'OFF',
596                         cls: 'slider-button',
597                         html: this.offtext
598                     }
599                 ]
600             };
601             
602             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
603                 cfg.cls += ' '+this.weight;
604             }
605             
606             return cfg;
607         }
608         
609         if (this.isClose) {
610             cfg.cls += ' close';
611             
612             cfg["aria-hidden"] = true;
613             
614             cfg.html = "&times;";
615             
616             return cfg;
617         }
618         
619          
620         if (this.theme==='default') {
621             cfg.cls = 'btn roo-button';
622             
623             //if (this.parentType != 'Navbar') {
624             this.weight = this.weight.length ?  this.weight : 'default';
625             //}
626             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
627                 
628                 cfg.cls += ' btn-' + this.weight;
629             }
630         } else if (this.theme==='glow') {
631             
632             cfg.tag = 'a';
633             cfg.cls = 'btn-glow roo-button';
634             
635             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
636                 
637                 cfg.cls += ' ' + this.weight;
638             }
639         }
640    
641         
642         if (this.inverse) {
643             this.cls += ' inverse';
644         }
645         
646         
647         if (this.active) {
648             cfg.cls += ' active';
649         }
650         
651         if (this.disabled) {
652             cfg.disabled = 'disabled';
653         }
654         
655         if (this.items) {
656             Roo.log('changing to ul' );
657             cfg.tag = 'ul';
658             this.glyphicon = 'caret';
659         }
660         
661         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
662          
663         //gsRoo.log(this.parentType);
664         if (this.parentType === 'Navbar' && !this.parent().bar) {
665             Roo.log('changing to li?');
666             
667             cfg.tag = 'li';
668             
669             cfg.cls = '';
670             cfg.cn =  [{
671                 tag : 'a',
672                 cls : 'roo-button',
673                 html : this.html,
674                 href : this.href || '#'
675             }];
676             if (this.menu) {
677                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
678                 cfg.cls += ' dropdown';
679             }   
680             
681             delete cfg.html;
682             
683         }
684         
685        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
686         
687         if (this.glyphicon) {
688             cfg.html = ' ' + cfg.html;
689             
690             cfg.cn = [
691                 {
692                     tag: 'span',
693                     cls: 'glyphicon glyphicon-' + this.glyphicon
694                 }
695             ];
696         }
697         
698         if (this.badge) {
699             cfg.html += ' ';
700             
701             cfg.tag = 'a';
702             
703 //            cfg.cls='btn roo-button';
704             
705             cfg.href=this.href;
706             
707             var value = cfg.html;
708             
709             if(this.glyphicon){
710                 value = {
711                             tag: 'span',
712                             cls: 'glyphicon glyphicon-' + this.glyphicon,
713                             html: this.html
714                         };
715                 
716             }
717             
718             cfg.cn = [
719                 value,
720                 {
721                     tag: 'span',
722                     cls: 'badge',
723                     html: this.badge
724                 }
725             ];
726             
727             cfg.html='';
728         }
729         
730         if (this.menu) {
731             cfg.cls += ' dropdown';
732             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
733         }
734         
735         if (cfg.tag !== 'a' && this.href !== '') {
736             throw "Tag must be a to set href.";
737         } else if (this.href.length > 0) {
738             cfg.href = this.href;
739         }
740         
741         if(this.removeClass){
742             cfg.cls = '';
743         }
744         
745         if(this.target){
746             cfg.target = this.target;
747         }
748         
749         return cfg;
750     },
751     initEvents: function() {
752        // Roo.log('init events?');
753 //        Roo.log(this.el.dom);
754         // add the menu...
755         
756         if (typeof (this.menu) != 'undefined') {
757             this.menu.parentType = this.xtype;
758             this.menu.triggerEl = this.el;
759             this.addxtype(Roo.apply({}, this.menu));
760         }
761
762
763        if (this.el.hasClass('roo-button')) {
764             this.el.on('click', this.onClick, this);
765        } else {
766             this.el.select('.roo-button').on('click', this.onClick, this);
767        }
768        
769        if(this.removeClass){
770            this.el.on('click', this.onClick, this);
771        }
772        
773        this.el.enableDisplayMode();
774         
775     },
776     onClick : function(e)
777     {
778         if (this.disabled) {
779             return;
780         }
781         
782         
783         Roo.log('button on click ');
784         if(this.preventDefault){
785             e.preventDefault();
786         }
787         if (this.pressed === true || this.pressed === false) {
788             this.pressed = !this.pressed;
789             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
790             this.fireEvent('toggle', this, e, this.pressed);
791         }
792         
793         
794         this.fireEvent('click', this, e);
795     },
796     
797     /**
798      * Enables this button
799      */
800     enable : function()
801     {
802         this.disabled = false;
803         this.el.removeClass('disabled');
804     },
805     
806     /**
807      * Disable this button
808      */
809     disable : function()
810     {
811         this.disabled = true;
812         this.el.addClass('disabled');
813     },
814      /**
815      * sets the active state on/off, 
816      * @param {Boolean} state (optional) Force a particular state
817      */
818     setActive : function(v) {
819         
820         this.el[v ? 'addClass' : 'removeClass']('active');
821     },
822      /**
823      * toggles the current active state 
824      */
825     toggleActive : function()
826     {
827        var active = this.el.hasClass('active');
828        this.setActive(!active);
829        
830         
831     },
832     setText : function(str)
833     {
834         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835     },
836     getText : function()
837     {
838         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
839     },
840     hide: function() {
841        
842      
843         this.el.hide();   
844     },
845     show: function() {
846        
847         this.el.show();   
848     }
849     
850     
851 });
852
853  /*
854  * - LGPL
855  *
856  * column
857  * 
858  */
859
860 /**
861  * @class Roo.bootstrap.Column
862  * @extends Roo.bootstrap.Component
863  * Bootstrap Column class
864  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
865  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
866  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
867  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
868  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
869  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
870  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
871  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
872  *
873  * 
874  * @cfg {Boolean} hidden (true|false) hide the element
875  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
876  * @cfg {String} fa (ban|check|...) font awesome icon
877  * @cfg {Number} fasize (1|2|....) font awsome size
878
879  * @cfg {String} icon (info-sign|check|...) glyphicon name
880
881  * @cfg {String} html content of column.
882  * 
883  * @constructor
884  * Create a new Column
885  * @param {Object} config The config object
886  */
887
888 Roo.bootstrap.Column = function(config){
889     Roo.bootstrap.Column.superclass.constructor.call(this, config);
890 };
891
892 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
893     
894     xs: false,
895     sm: false,
896     md: false,
897     lg: false,
898     xsoff: false,
899     smoff: false,
900     mdoff: false,
901     lgoff: false,
902     html: '',
903     offset: 0,
904     alert: false,
905     fa: false,
906     icon : false,
907     hidden : false,
908     fasize : 1,
909     
910     getAutoCreate : function(){
911         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
912         
913         cfg = {
914             tag: 'div',
915             cls: 'column'
916         };
917         
918         var settings=this;
919         ['xs','sm','md','lg'].map(function(size){
920             //Roo.log( size + ':' + settings[size]);
921             
922             if (settings[size+'off'] !== false) {
923                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
924             }
925             
926             if (settings[size] === false) {
927                 return;
928             }
929             
930             if (!settings[size]) { // 0 = hidden
931                 cfg.cls += ' hidden-' + size;
932                 return;
933             }
934             cfg.cls += ' col-' + size + '-' + settings[size];
935             
936         });
937         
938         if (this.hidden) {
939             cfg.cls += ' hidden';
940         }
941         
942         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
943             cfg.cls +=' alert alert-' + this.alert;
944         }
945         
946         
947         if (this.html.length) {
948             cfg.html = this.html;
949         }
950         if (this.fa) {
951             var fasize = '';
952             if (this.fasize > 1) {
953                 fasize = ' fa-' + this.fasize + 'x';
954             }
955             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
956             
957             
958         }
959         if (this.icon) {
960             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
961         }
962         
963         return cfg;
964     }
965    
966 });
967
968  
969
970  /*
971  * - LGPL
972  *
973  * page container.
974  * 
975  */
976
977
978 /**
979  * @class Roo.bootstrap.Container
980  * @extends Roo.bootstrap.Component
981  * Bootstrap Container class
982  * @cfg {Boolean} jumbotron is it a jumbotron element
983  * @cfg {String} html content of element
984  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
985  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
986  * @cfg {String} header content of header (for panel)
987  * @cfg {String} footer content of footer (for panel)
988  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
989  * @cfg {String} tag (header|aside|section) type of HTML tag.
990  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
991  * @cfg {String} fa font awesome icon
992  * @cfg {String} icon (info-sign|check|...) glyphicon name
993  * @cfg {Boolean} hidden (true|false) hide the element
994  * @cfg {Boolean} expandable (true|false) default false
995  * @cfg {Boolean} expanded (true|false) default true
996  * @cfg {String} rheader contet on the right of header
997  * @cfg {Boolean} clickable (true|false) default false
998
999  *     
1000  * @constructor
1001  * Create a new Container
1002  * @param {Object} config The config object
1003  */
1004
1005 Roo.bootstrap.Container = function(config){
1006     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1007     
1008     this.addEvents({
1009         // raw events
1010          /**
1011          * @event expand
1012          * After the panel has been expand
1013          * 
1014          * @param {Roo.bootstrap.Container} this
1015          */
1016         "expand" : true,
1017         /**
1018          * @event collapse
1019          * After the panel has been collapsed
1020          * 
1021          * @param {Roo.bootstrap.Container} this
1022          */
1023         "collapse" : true,
1024         /**
1025          * @event click
1026          * When a element is chick
1027          * @param {Roo.bootstrap.Container} this
1028          * @param {Roo.EventObject} e
1029          */
1030         "click" : true
1031     });
1032 };
1033
1034 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1035     
1036     jumbotron : false,
1037     well: '',
1038     panel : '',
1039     header: '',
1040     footer : '',
1041     sticky: '',
1042     tag : false,
1043     alert : false,
1044     fa: false,
1045     icon : false,
1046     expandable : false,
1047     rheader : '',
1048     expanded : true,
1049     clickable: false,
1050   
1051      
1052     getChildContainer : function() {
1053         
1054         if(!this.el){
1055             return false;
1056         }
1057         
1058         if (this.panel.length) {
1059             return this.el.select('.panel-body',true).first();
1060         }
1061         
1062         return this.el;
1063     },
1064     
1065     
1066     getAutoCreate : function(){
1067         
1068         var cfg = {
1069             tag : this.tag || 'div',
1070             html : '',
1071             cls : ''
1072         };
1073         if (this.jumbotron) {
1074             cfg.cls = 'jumbotron';
1075         }
1076         
1077         
1078         
1079         // - this is applied by the parent..
1080         //if (this.cls) {
1081         //    cfg.cls = this.cls + '';
1082         //}
1083         
1084         if (this.sticky.length) {
1085             
1086             var bd = Roo.get(document.body);
1087             if (!bd.hasClass('bootstrap-sticky')) {
1088                 bd.addClass('bootstrap-sticky');
1089                 Roo.select('html',true).setStyle('height', '100%');
1090             }
1091              
1092             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1093         }
1094         
1095         
1096         if (this.well.length) {
1097             switch (this.well) {
1098                 case 'lg':
1099                 case 'sm':
1100                     cfg.cls +=' well well-' +this.well;
1101                     break;
1102                 default:
1103                     cfg.cls +=' well';
1104                     break;
1105             }
1106         }
1107         
1108         if (this.hidden) {
1109             cfg.cls += ' hidden';
1110         }
1111         
1112         
1113         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1114             cfg.cls +=' alert alert-' + this.alert;
1115         }
1116         
1117         var body = cfg;
1118         
1119         if (this.panel.length) {
1120             cfg.cls += ' panel panel-' + this.panel;
1121             cfg.cn = [];
1122             if (this.header.length) {
1123                 
1124                 var h = [];
1125                 
1126                 if(this.expandable){
1127                     
1128                     cfg.cls = cfg.cls + ' expandable';
1129                     
1130                     h.push({
1131                         tag: 'i',
1132                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1133                     });
1134                     
1135                 }
1136                 
1137                 h.push(
1138                     {
1139                         tag: 'span',
1140                         cls : 'panel-title',
1141                         html : (this.expandable ? '&nbsp;' : '') + this.header
1142                     },
1143                     {
1144                         tag: 'span',
1145                         cls: 'panel-header-right',
1146                         html: this.rheader
1147                     }
1148                 );
1149                 
1150                 cfg.cn.push({
1151                     cls : 'panel-heading',
1152                     style : this.expandable ? 'cursor: pointer' : '',
1153                     cn : h
1154                 });
1155                 
1156             }
1157             
1158             body = false;
1159             cfg.cn.push({
1160                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1161                 html : this.html
1162             });
1163             
1164             
1165             if (this.footer.length) {
1166                 cfg.cn.push({
1167                     cls : 'panel-footer',
1168                     html : this.footer
1169                     
1170                 });
1171             }
1172             
1173         }
1174         
1175         if (body) {
1176             body.html = this.html || cfg.html;
1177             // prefix with the icons..
1178             if (this.fa) {
1179                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1180             }
1181             if (this.icon) {
1182                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1183             }
1184             
1185             
1186         }
1187         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1188             cfg.cls =  'container';
1189         }
1190         
1191         return cfg;
1192     },
1193     
1194     initEvents: function() 
1195     {
1196         if(this.expandable){
1197             var headerEl = this.headerEl();
1198         
1199             if(headerEl){
1200                 headerEl.on('click', this.onToggleClick, this);
1201             }
1202         }
1203         
1204         if(this.clickable){
1205             this.el.on('click', this.onClick, this);
1206         }
1207         
1208     },
1209     
1210     onToggleClick : function()
1211     {
1212         var headerEl = this.headerEl();
1213         
1214         if(!headerEl){
1215             return;
1216         }
1217         
1218         if(this.expanded){
1219             this.collapse();
1220             return;
1221         }
1222         
1223         this.expand();
1224     },
1225     
1226     expand : function()
1227     {
1228         if(this.fireEvent('expand', this)) {
1229             
1230             this.expanded = true;
1231             
1232             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1233             
1234             this.el.select('.panel-body',true).first().removeClass('hide');
1235             
1236             var toggleEl = this.toggleEl();
1237
1238             if(!toggleEl){
1239                 return;
1240             }
1241
1242             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1243         }
1244         
1245     },
1246     
1247     collapse : function()
1248     {
1249         if(this.fireEvent('collapse', this)) {
1250             
1251             this.expanded = false;
1252             
1253             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1254             this.el.select('.panel-body',true).first().addClass('hide');
1255         
1256             var toggleEl = this.toggleEl();
1257
1258             if(!toggleEl){
1259                 return;
1260             }
1261
1262             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1263         }
1264     },
1265     
1266     toggleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-heading .fa',true).first();
1273     },
1274     
1275     headerEl : function()
1276     {
1277         if(!this.el || !this.panel.length || !this.header.length){
1278             return;
1279         }
1280         
1281         return this.el.select('.panel-heading',true).first()
1282     },
1283     
1284     titleEl : function()
1285     {
1286         if(!this.el || !this.panel.length || !this.header.length){
1287             return;
1288         }
1289         
1290         return this.el.select('.panel-title',true).first();
1291     },
1292     
1293     setTitle : function(v)
1294     {
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return;
1299         }
1300         
1301         titleEl.dom.innerHTML = v;
1302     },
1303     
1304     getTitle : function()
1305     {
1306         
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return '';
1311         }
1312         
1313         return titleEl.dom.innerHTML;
1314     },
1315     
1316     setRightTitle : function(v)
1317     {
1318         var t = this.el.select('.panel-header-right',true).first();
1319         
1320         if(!t){
1321             return;
1322         }
1323         
1324         t.dom.innerHTML = v;
1325     },
1326     
1327     onClick : function(e)
1328     {
1329         e.preventDefault();
1330         
1331         this.fireEvent('click', this, e);
1332     }
1333    
1334 });
1335
1336  /*
1337  * - LGPL
1338  *
1339  * image
1340  * 
1341  */
1342
1343
1344 /**
1345  * @class Roo.bootstrap.Img
1346  * @extends Roo.bootstrap.Component
1347  * Bootstrap Img class
1348  * @cfg {Boolean} imgResponsive false | true
1349  * @cfg {String} border rounded | circle | thumbnail
1350  * @cfg {String} src image source
1351  * @cfg {String} alt image alternative text
1352  * @cfg {String} href a tag href
1353  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1354  * @cfg {String} xsUrl xs image source
1355  * @cfg {String} smUrl sm image source
1356  * @cfg {String} mdUrl md image source
1357  * @cfg {String} lgUrl lg image source
1358  * 
1359  * @constructor
1360  * Create a new Input
1361  * @param {Object} config The config object
1362  */
1363
1364 Roo.bootstrap.Img = function(config){
1365     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1366     
1367     this.addEvents({
1368         // img events
1369         /**
1370          * @event click
1371          * The img click event for the img.
1372          * @param {Roo.EventObject} e
1373          */
1374         "click" : true
1375     });
1376 };
1377
1378 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1379     
1380     imgResponsive: true,
1381     border: '',
1382     src: 'about:blank',
1383     href: false,
1384     target: false,
1385     xsUrl: '',
1386     smUrl: '',
1387     mdUrl: '',
1388     lgUrl: '',
1389
1390     getAutoCreate : function()
1391     {   
1392         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1393             return this.createSingleImg();
1394         }
1395         
1396         var cfg = {
1397             tag: 'div',
1398             cls: 'roo-image-responsive-group',
1399             cn: []
1400         };
1401         var _this = this;
1402         
1403         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1404             
1405             if(!_this[size + 'Url']){
1406                 return;
1407             }
1408             
1409             var img = {
1410                 tag: 'img',
1411                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1412                 html: _this.html || cfg.html,
1413                 src: _this[size + 'Url']
1414             };
1415             
1416             img.cls += ' roo-image-responsive-' + size;
1417             
1418             var s = ['xs', 'sm', 'md', 'lg'];
1419             
1420             s.splice(s.indexOf(size), 1);
1421             
1422             Roo.each(s, function(ss){
1423                 img.cls += ' hidden-' + ss;
1424             });
1425             
1426             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1427                 cfg.cls += ' img-' + _this.border;
1428             }
1429             
1430             if(_this.alt){
1431                 cfg.alt = _this.alt;
1432             }
1433             
1434             if(_this.href){
1435                 var a = {
1436                     tag: 'a',
1437                     href: _this.href,
1438                     cn: [
1439                         img
1440                     ]
1441                 };
1442
1443                 if(this.target){
1444                     a.target = _this.target;
1445                 }
1446             }
1447             
1448             cfg.cn.push((_this.href) ? a : img);
1449             
1450         });
1451         
1452         return cfg;
1453     },
1454     
1455     createSingleImg : function()
1456     {
1457         var cfg = {
1458             tag: 'img',
1459             cls: (this.imgResponsive) ? 'img-responsive' : '',
1460             html : null,
1461             src : 'about:blank'  // just incase src get's set to undefined?!?
1462         };
1463         
1464         cfg.html = this.html || cfg.html;
1465         
1466         cfg.src = this.src || cfg.src;
1467         
1468         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1469             cfg.cls += ' img-' + this.border;
1470         }
1471         
1472         if(this.alt){
1473             cfg.alt = this.alt;
1474         }
1475         
1476         if(this.href){
1477             var a = {
1478                 tag: 'a',
1479                 href: this.href,
1480                 cn: [
1481                     cfg
1482                 ]
1483             };
1484             
1485             if(this.target){
1486                 a.target = this.target;
1487             }
1488             
1489         }
1490         
1491         return (this.href) ? a : cfg;
1492     },
1493     
1494     initEvents: function() 
1495     {
1496         if(!this.href){
1497             this.el.on('click', this.onClick, this);
1498         }
1499         
1500     },
1501     
1502     onClick : function(e)
1503     {
1504         Roo.log('img onclick');
1505         this.fireEvent('click', this, e);
1506     }
1507    
1508 });
1509
1510  /*
1511  * - LGPL
1512  *
1513  * image
1514  * 
1515  */
1516
1517
1518 /**
1519  * @class Roo.bootstrap.Link
1520  * @extends Roo.bootstrap.Component
1521  * Bootstrap Link Class
1522  * @cfg {String} alt image alternative text
1523  * @cfg {String} href a tag href
1524  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1525  * @cfg {String} html the content of the link.
1526  * @cfg {String} anchor name for the anchor link
1527
1528  * @cfg {Boolean} preventDefault (true | false) default false
1529
1530  * 
1531  * @constructor
1532  * Create a new Input
1533  * @param {Object} config The config object
1534  */
1535
1536 Roo.bootstrap.Link = function(config){
1537     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1538     
1539     this.addEvents({
1540         // img events
1541         /**
1542          * @event click
1543          * The img click event for the img.
1544          * @param {Roo.EventObject} e
1545          */
1546         "click" : true
1547     });
1548 };
1549
1550 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1551     
1552     href: false,
1553     target: false,
1554     preventDefault: false,
1555     anchor : false,
1556     alt : false,
1557
1558     getAutoCreate : function()
1559     {
1560         
1561         var cfg = {
1562             tag: 'a'
1563         };
1564         // anchor's do not require html/href...
1565         if (this.anchor === false) {
1566             cfg.html = this.html || '';
1567             cfg.href = this.href || '#';
1568         } else {
1569             cfg.name = this.anchor;
1570             if (this.html !== false) {
1571                 cfg.html = this.html;
1572             }
1573             if (this.href !== false) {
1574                 cfg.href = this.href;
1575             }
1576         }
1577         
1578         if(this.alt !== false){
1579             cfg.alt = this.alt;
1580         }
1581         
1582         
1583         if(this.target !== false) {
1584             cfg.target = this.target;
1585         }
1586         
1587         return cfg;
1588     },
1589     
1590     initEvents: function() {
1591         
1592         if(!this.href || this.preventDefault){
1593             this.el.on('click', this.onClick, this);
1594         }
1595     },
1596     
1597     onClick : function(e)
1598     {
1599         if(this.preventDefault){
1600             e.preventDefault();
1601         }
1602         //Roo.log('img onclick');
1603         this.fireEvent('click', this, e);
1604     }
1605    
1606 });
1607
1608  /*
1609  * - LGPL
1610  *
1611  * header
1612  * 
1613  */
1614
1615 /**
1616  * @class Roo.bootstrap.Header
1617  * @extends Roo.bootstrap.Component
1618  * Bootstrap Header class
1619  * @cfg {String} html content of header
1620  * @cfg {Number} level (1|2|3|4|5|6) default 1
1621  * 
1622  * @constructor
1623  * Create a new Header
1624  * @param {Object} config The config object
1625  */
1626
1627
1628 Roo.bootstrap.Header  = function(config){
1629     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1630 };
1631
1632 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1633     
1634     //href : false,
1635     html : false,
1636     level : 1,
1637     
1638     
1639     
1640     getAutoCreate : function(){
1641         
1642         
1643         
1644         var cfg = {
1645             tag: 'h' + (1 *this.level),
1646             html: this.html || ''
1647         } ;
1648         
1649         return cfg;
1650     }
1651    
1652 });
1653
1654  
1655
1656  /*
1657  * Based on:
1658  * Ext JS Library 1.1.1
1659  * Copyright(c) 2006-2007, Ext JS, LLC.
1660  *
1661  * Originally Released Under LGPL - original licence link has changed is not relivant.
1662  *
1663  * Fork - LGPL
1664  * <script type="text/javascript">
1665  */
1666  
1667 /**
1668  * @class Roo.bootstrap.MenuMgr
1669  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1670  * @singleton
1671  */
1672 Roo.bootstrap.MenuMgr = function(){
1673    var menus, active, groups = {}, attached = false, lastShow = new Date();
1674
1675    // private - called when first menu is created
1676    function init(){
1677        menus = {};
1678        active = new Roo.util.MixedCollection();
1679        Roo.get(document).addKeyListener(27, function(){
1680            if(active.length > 0){
1681                hideAll();
1682            }
1683        });
1684    }
1685
1686    // private
1687    function hideAll(){
1688        if(active && active.length > 0){
1689            var c = active.clone();
1690            c.each(function(m){
1691                m.hide();
1692            });
1693        }
1694    }
1695
1696    // private
1697    function onHide(m){
1698        active.remove(m);
1699        if(active.length < 1){
1700            Roo.get(document).un("mouseup", onMouseDown);
1701             
1702            attached = false;
1703        }
1704    }
1705
1706    // private
1707    function onShow(m){
1708        var last = active.last();
1709        lastShow = new Date();
1710        active.add(m);
1711        if(!attached){
1712           Roo.get(document).on("mouseup", onMouseDown);
1713            
1714            attached = true;
1715        }
1716        if(m.parentMenu){
1717           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1718           m.parentMenu.activeChild = m;
1719        }else if(last && last.isVisible()){
1720           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1721        }
1722    }
1723
1724    // private
1725    function onBeforeHide(m){
1726        if(m.activeChild){
1727            m.activeChild.hide();
1728        }
1729        if(m.autoHideTimer){
1730            clearTimeout(m.autoHideTimer);
1731            delete m.autoHideTimer;
1732        }
1733    }
1734
1735    // private
1736    function onBeforeShow(m){
1737        var pm = m.parentMenu;
1738        if(!pm && !m.allowOtherMenus){
1739            hideAll();
1740        }else if(pm && pm.activeChild && active != m){
1741            pm.activeChild.hide();
1742        }
1743    }
1744
1745    // private this should really trigger on mouseup..
1746    function onMouseDown(e){
1747         Roo.log("on Mouse Up");
1748         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1749             Roo.log("hideAll");
1750             hideAll();
1751             e.stopEvent();
1752         }
1753         
1754         
1755    }
1756
1757    // private
1758    function onBeforeCheck(mi, state){
1759        if(state){
1760            var g = groups[mi.group];
1761            for(var i = 0, l = g.length; i < l; i++){
1762                if(g[i] != mi){
1763                    g[i].setChecked(false);
1764                }
1765            }
1766        }
1767    }
1768
1769    return {
1770
1771        /**
1772         * Hides all menus that are currently visible
1773         */
1774        hideAll : function(){
1775             hideAll();  
1776        },
1777
1778        // private
1779        register : function(menu){
1780            if(!menus){
1781                init();
1782            }
1783            menus[menu.id] = menu;
1784            menu.on("beforehide", onBeforeHide);
1785            menu.on("hide", onHide);
1786            menu.on("beforeshow", onBeforeShow);
1787            menu.on("show", onShow);
1788            var g = menu.group;
1789            if(g && menu.events["checkchange"]){
1790                if(!groups[g]){
1791                    groups[g] = [];
1792                }
1793                groups[g].push(menu);
1794                menu.on("checkchange", onCheck);
1795            }
1796        },
1797
1798         /**
1799          * Returns a {@link Roo.menu.Menu} object
1800          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1801          * be used to generate and return a new Menu instance.
1802          */
1803        get : function(menu){
1804            if(typeof menu == "string"){ // menu id
1805                return menus[menu];
1806            }else if(menu.events){  // menu instance
1807                return menu;
1808            }
1809            /*else if(typeof menu.length == 'number'){ // array of menu items?
1810                return new Roo.bootstrap.Menu({items:menu});
1811            }else{ // otherwise, must be a config
1812                return new Roo.bootstrap.Menu(menu);
1813            }
1814            */
1815            return false;
1816        },
1817
1818        // private
1819        unregister : function(menu){
1820            delete menus[menu.id];
1821            menu.un("beforehide", onBeforeHide);
1822            menu.un("hide", onHide);
1823            menu.un("beforeshow", onBeforeShow);
1824            menu.un("show", onShow);
1825            var g = menu.group;
1826            if(g && menu.events["checkchange"]){
1827                groups[g].remove(menu);
1828                menu.un("checkchange", onCheck);
1829            }
1830        },
1831
1832        // private
1833        registerCheckable : function(menuItem){
1834            var g = menuItem.group;
1835            if(g){
1836                if(!groups[g]){
1837                    groups[g] = [];
1838                }
1839                groups[g].push(menuItem);
1840                menuItem.on("beforecheckchange", onBeforeCheck);
1841            }
1842        },
1843
1844        // private
1845        unregisterCheckable : function(menuItem){
1846            var g = menuItem.group;
1847            if(g){
1848                groups[g].remove(menuItem);
1849                menuItem.un("beforecheckchange", onBeforeCheck);
1850            }
1851        }
1852    };
1853 }();/*
1854  * - LGPL
1855  *
1856  * menu
1857  * 
1858  */
1859
1860 /**
1861  * @class Roo.bootstrap.Menu
1862  * @extends Roo.bootstrap.Component
1863  * Bootstrap Menu class - container for MenuItems
1864  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1865  * 
1866  * @constructor
1867  * Create a new Menu
1868  * @param {Object} config The config object
1869  */
1870
1871
1872 Roo.bootstrap.Menu = function(config){
1873     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1874     if (this.registerMenu) {
1875         Roo.bootstrap.MenuMgr.register(this);
1876     }
1877     this.addEvents({
1878         /**
1879          * @event beforeshow
1880          * Fires before this menu is displayed
1881          * @param {Roo.menu.Menu} this
1882          */
1883         beforeshow : true,
1884         /**
1885          * @event beforehide
1886          * Fires before this menu is hidden
1887          * @param {Roo.menu.Menu} this
1888          */
1889         beforehide : true,
1890         /**
1891          * @event show
1892          * Fires after this menu is displayed
1893          * @param {Roo.menu.Menu} this
1894          */
1895         show : true,
1896         /**
1897          * @event hide
1898          * Fires after this menu is hidden
1899          * @param {Roo.menu.Menu} this
1900          */
1901         hide : true,
1902         /**
1903          * @event click
1904          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1905          * @param {Roo.menu.Menu} this
1906          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1907          * @param {Roo.EventObject} e
1908          */
1909         click : true,
1910         /**
1911          * @event mouseover
1912          * Fires when the mouse is hovering over this menu
1913          * @param {Roo.menu.Menu} this
1914          * @param {Roo.EventObject} e
1915          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1916          */
1917         mouseover : true,
1918         /**
1919          * @event mouseout
1920          * Fires when the mouse exits this menu
1921          * @param {Roo.menu.Menu} this
1922          * @param {Roo.EventObject} e
1923          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1924          */
1925         mouseout : true,
1926         /**
1927          * @event itemclick
1928          * Fires when a menu item contained in this menu is clicked
1929          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1930          * @param {Roo.EventObject} e
1931          */
1932         itemclick: true
1933     });
1934     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1935 };
1936
1937 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1938     
1939    /// html : false,
1940     //align : '',
1941     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1942     type: false,
1943     /**
1944      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1945      */
1946     registerMenu : true,
1947     
1948     menuItems :false, // stores the menu items..
1949     
1950     hidden:true,
1951     
1952     parentMenu : false,
1953     
1954     getChildContainer : function() {
1955         return this.el;  
1956     },
1957     
1958     getAutoCreate : function(){
1959          
1960         //if (['right'].indexOf(this.align)!==-1) {
1961         //    cfg.cn[1].cls += ' pull-right'
1962         //}
1963         
1964         
1965         var cfg = {
1966             tag : 'ul',
1967             cls : 'dropdown-menu' ,
1968             style : 'z-index:1000'
1969             
1970         };
1971         
1972         if (this.type === 'submenu') {
1973             cfg.cls = 'submenu active';
1974         }
1975         if (this.type === 'treeview') {
1976             cfg.cls = 'treeview-menu';
1977         }
1978         
1979         return cfg;
1980     },
1981     initEvents : function() {
1982         
1983        // Roo.log("ADD event");
1984        // Roo.log(this.triggerEl.dom);
1985         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1986         
1987         this.triggerEl.addClass('dropdown-toggle');
1988         
1989         if (Roo.isTouch) {
1990             this.el.on('touchstart'  , this.onTouch, this);
1991         }
1992         this.el.on('click' , this.onClick, this);
1993
1994         this.el.on("mouseover", this.onMouseOver, this);
1995         this.el.on("mouseout", this.onMouseOut, this);
1996         
1997     },
1998     
1999     findTargetItem : function(e)
2000     {
2001         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2002         if(!t){
2003             return false;
2004         }
2005         //Roo.log(t);         Roo.log(t.id);
2006         if(t && t.id){
2007             //Roo.log(this.menuitems);
2008             return this.menuitems.get(t.id);
2009             
2010             //return this.items.get(t.menuItemId);
2011         }
2012         
2013         return false;
2014     },
2015     
2016     onTouch : function(e) 
2017     {
2018         //e.stopEvent(); this make the user popdown broken
2019         this.onClick(e);
2020     },
2021     
2022     onClick : function(e)
2023     {
2024         Roo.log("menu.onClick");
2025         var t = this.findTargetItem(e);
2026         if(!t || t.isContainer){
2027             return;
2028         }
2029         Roo.log(e);
2030         /*
2031         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2032             if(t == this.activeItem && t.shouldDeactivate(e)){
2033                 this.activeItem.deactivate();
2034                 delete this.activeItem;
2035                 return;
2036             }
2037             if(t.canActivate){
2038                 this.setActiveItem(t, true);
2039             }
2040             return;
2041             
2042             
2043         }
2044         */
2045        
2046         Roo.log('pass click event');
2047         
2048         t.onClick(e);
2049         
2050         this.fireEvent("click", this, t, e);
2051         
2052         this.hide();
2053     },
2054      onMouseOver : function(e){
2055         var t  = this.findTargetItem(e);
2056         //Roo.log(t);
2057         //if(t){
2058         //    if(t.canActivate && !t.disabled){
2059         //        this.setActiveItem(t, true);
2060         //    }
2061         //}
2062         
2063         this.fireEvent("mouseover", this, e, t);
2064     },
2065     isVisible : function(){
2066         return !this.hidden;
2067     },
2068      onMouseOut : function(e){
2069         var t  = this.findTargetItem(e);
2070         
2071         //if(t ){
2072         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2073         //        this.activeItem.deactivate();
2074         //        delete this.activeItem;
2075         //    }
2076         //}
2077         this.fireEvent("mouseout", this, e, t);
2078     },
2079     
2080     
2081     /**
2082      * Displays this menu relative to another element
2083      * @param {String/HTMLElement/Roo.Element} element The element to align to
2084      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2085      * the element (defaults to this.defaultAlign)
2086      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2087      */
2088     show : function(el, pos, parentMenu){
2089         this.parentMenu = parentMenu;
2090         if(!this.el){
2091             this.render();
2092         }
2093         this.fireEvent("beforeshow", this);
2094         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2095     },
2096      /**
2097      * Displays this menu at a specific xy position
2098      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2099      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2100      */
2101     showAt : function(xy, parentMenu, /* private: */_e){
2102         this.parentMenu = parentMenu;
2103         if(!this.el){
2104             this.render();
2105         }
2106         if(_e !== false){
2107             this.fireEvent("beforeshow", this);
2108             //xy = this.el.adjustForConstraints(xy);
2109         }
2110         
2111         //this.el.show();
2112         this.hideMenuItems();
2113         this.hidden = false;
2114         this.triggerEl.addClass('open');
2115         
2116         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2117             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2118         }
2119         
2120         this.el.setXY(xy);
2121         this.focus();
2122         this.fireEvent("show", this);
2123     },
2124     
2125     focus : function(){
2126         return;
2127         if(!this.hidden){
2128             this.doFocus.defer(50, this);
2129         }
2130     },
2131
2132     doFocus : function(){
2133         if(!this.hidden){
2134             this.focusEl.focus();
2135         }
2136     },
2137
2138     /**
2139      * Hides this menu and optionally all parent menus
2140      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2141      */
2142     hide : function(deep){
2143         
2144         this.hideMenuItems();
2145         if(this.el && this.isVisible()){
2146             this.fireEvent("beforehide", this);
2147             if(this.activeItem){
2148                 this.activeItem.deactivate();
2149                 this.activeItem = null;
2150             }
2151             this.triggerEl.removeClass('open');;
2152             this.hidden = true;
2153             this.fireEvent("hide", this);
2154         }
2155         if(deep === true && this.parentMenu){
2156             this.parentMenu.hide(true);
2157         }
2158     },
2159     
2160     onTriggerPress  : function(e)
2161     {
2162         
2163         Roo.log('trigger press');
2164         //Roo.log(e.getTarget());
2165        // Roo.log(this.triggerEl.dom);
2166         if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
2167             return;
2168         }
2169         
2170         if (this.isVisible()) {
2171             Roo.log('hide');
2172             this.hide();
2173         } else {
2174             Roo.log('show');
2175             this.show(this.triggerEl, false, false);
2176         }
2177         
2178         e.stopEvent();
2179     },
2180     
2181          
2182        
2183     
2184     hideMenuItems : function()
2185     {
2186         //$(backdrop).remove()
2187         Roo.select('.open',true).each(function(aa) {
2188             
2189             aa.removeClass('open');
2190           //var parent = getParent($(this))
2191           //var relatedTarget = { relatedTarget: this }
2192           
2193            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2194           //if (e.isDefaultPrevented()) return
2195            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2196         })
2197     },
2198     addxtypeChild : function (tree, cntr) {
2199         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2200           
2201         this.menuitems.add(comp);
2202         return comp;
2203
2204     },
2205     getEl : function()
2206     {
2207         Roo.log(this.el);
2208         return this.el;
2209     }
2210 });
2211
2212  
2213  /*
2214  * - LGPL
2215  *
2216  * menu item
2217  * 
2218  */
2219
2220
2221 /**
2222  * @class Roo.bootstrap.MenuItem
2223  * @extends Roo.bootstrap.Component
2224  * Bootstrap MenuItem class
2225  * @cfg {String} html the menu label
2226  * @cfg {String} href the link
2227  * @cfg {Boolean} preventDefault (true | false) default true
2228  * @cfg {Boolean} isContainer (true | false) default false
2229  * 
2230  * 
2231  * @constructor
2232  * Create a new MenuItem
2233  * @param {Object} config The config object
2234  */
2235
2236
2237 Roo.bootstrap.MenuItem = function(config){
2238     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2239     this.addEvents({
2240         // raw events
2241         /**
2242          * @event click
2243          * The raw click event for the entire grid.
2244          * @param {Roo.bootstrap.MenuItem} this
2245          * @param {Roo.EventObject} e
2246          */
2247         "click" : true
2248     });
2249 };
2250
2251 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2252     
2253     href : false,
2254     html : false,
2255     preventDefault: true,
2256     isContainer : false,
2257     
2258     getAutoCreate : function(){
2259         
2260         if(this.isContainer){
2261             return {
2262                 tag: 'li',
2263                 cls: 'dropdown-menu-item'
2264             };
2265         }
2266         
2267         var cfg= {
2268             tag: 'li',
2269             cls: 'dropdown-menu-item',
2270             cn: [
2271                     {
2272                         tag : 'a',
2273                         href : '#',
2274                         html : 'Link'
2275                     }
2276                 ]
2277         };
2278         if (this.parent().type == 'treeview') {
2279             cfg.cls = 'treeview-menu';
2280         }
2281         
2282         cfg.cn[0].href = this.href || cfg.cn[0].href ;
2283         cfg.cn[0].html = this.html || cfg.cn[0].html ;
2284         return cfg;
2285     },
2286     
2287     initEvents: function() {
2288         
2289         //this.el.select('a').on('click', this.onClick, this);
2290         
2291     },
2292     onClick : function(e)
2293     {
2294         Roo.log('item on click ');
2295         //if(this.preventDefault){
2296         //    e.preventDefault();
2297         //}
2298         //this.parent().hideMenuItems();
2299         
2300         this.fireEvent('click', this, e);
2301     },
2302     getEl : function()
2303     {
2304         return this.el;
2305     }
2306 });
2307
2308  
2309
2310  /*
2311  * - LGPL
2312  *
2313  * menu separator
2314  * 
2315  */
2316
2317
2318 /**
2319  * @class Roo.bootstrap.MenuSeparator
2320  * @extends Roo.bootstrap.Component
2321  * Bootstrap MenuSeparator class
2322  * 
2323  * @constructor
2324  * Create a new MenuItem
2325  * @param {Object} config The config object
2326  */
2327
2328
2329 Roo.bootstrap.MenuSeparator = function(config){
2330     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2331 };
2332
2333 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2334     
2335     getAutoCreate : function(){
2336         var cfg = {
2337             cls: 'divider',
2338             tag : 'li'
2339         };
2340         
2341         return cfg;
2342     }
2343    
2344 });
2345
2346  
2347
2348  
2349 /*
2350 * Licence: LGPL
2351 */
2352
2353 /**
2354  * @class Roo.bootstrap.Modal
2355  * @extends Roo.bootstrap.Component
2356  * Bootstrap Modal class
2357  * @cfg {String} title Title of dialog
2358  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2359  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2360  * @cfg {Boolean} specificTitle default false
2361  * @cfg {Array} buttons Array of buttons or standard button set..
2362  * @cfg {String} buttonPosition (left|right|center) default right
2363  * @cfg {Boolean} animate default true
2364  * @cfg {Boolean} allow_close default true
2365  * 
2366  * @constructor
2367  * Create a new Modal Dialog
2368  * @param {Object} config The config object
2369  */
2370
2371 Roo.bootstrap.Modal = function(config){
2372     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2373     this.addEvents({
2374         // raw events
2375         /**
2376          * @event btnclick
2377          * The raw btnclick event for the button
2378          * @param {Roo.EventObject} e
2379          */
2380         "btnclick" : true
2381     });
2382     this.buttons = this.buttons || [];
2383      
2384     if (this.tmpl) {
2385         this.tmpl = Roo.factory(this.tmpl);
2386     }
2387     
2388 };
2389
2390 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2391     
2392     title : 'test dialog',
2393    
2394     buttons : false,
2395     
2396     // set on load...
2397      
2398     html: false,
2399     
2400     tmp: false,
2401     
2402     specificTitle: false,
2403     
2404     buttonPosition: 'right',
2405     
2406     allow_close : true,
2407     
2408     animate : true,
2409     
2410     
2411      // private
2412     bodyEl:  false,
2413     footerEl:  false,
2414     titleEl:  false,
2415     closeEl:  false,
2416     
2417     
2418     onRender : function(ct, position)
2419     {
2420         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2421      
2422         if(!this.el){
2423             var cfg = Roo.apply({},  this.getAutoCreate());
2424             cfg.id = Roo.id();
2425             //if(!cfg.name){
2426             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2427             //}
2428             //if (!cfg.name.length) {
2429             //    delete cfg.name;
2430            // }
2431             if (this.cls) {
2432                 cfg.cls += ' ' + this.cls;
2433             }
2434             if (this.style) {
2435                 cfg.style = this.style;
2436             }
2437             this.el = Roo.get(document.body).createChild(cfg, position);
2438         }
2439         //var type = this.el.dom.type;
2440         
2441         
2442         if(this.tabIndex !== undefined){
2443             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2444         }
2445         
2446         
2447         this.bodyEl = this.el.select('.modal-body',true).first();
2448         this.closeEl = this.el.select('.modal-header .close', true).first();
2449         this.footerEl = this.el.select('.modal-footer',true).first();
2450         this.titleEl = this.el.select('.modal-title',true).first();
2451         
2452         
2453          
2454         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2455         this.maskEl.enableDisplayMode("block");
2456         this.maskEl.hide();
2457         //this.el.addClass("x-dlg-modal");
2458     
2459         if (this.buttons.length) {
2460             Roo.each(this.buttons, function(bb) {
2461                 var b = Roo.apply({}, bb);
2462                 b.xns = b.xns || Roo.bootstrap;
2463                 b.xtype = b.xtype || 'Button';
2464                 if (typeof(b.listeners) == 'undefined') {
2465                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2466                 }
2467                 
2468                 var btn = Roo.factory(b);
2469                 
2470                 btn.onRender(this.el.select('.modal-footer div').first());
2471                 
2472             },this);
2473         }
2474         // render the children.
2475         var nitems = [];
2476         
2477         if(typeof(this.items) != 'undefined'){
2478             var items = this.items;
2479             delete this.items;
2480
2481             for(var i =0;i < items.length;i++) {
2482                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2483             }
2484         }
2485         
2486         this.items = nitems;
2487         
2488         // where are these used - they used to be body/close/footer
2489         
2490        
2491         this.initEvents();
2492         //this.el.addClass([this.fieldClass, this.cls]);
2493         
2494     },
2495     
2496     getAutoCreate : function(){
2497         
2498         
2499         var bdy = {
2500                 cls : 'modal-body',
2501                 html : this.html || ''
2502         };
2503         
2504         var title = {
2505             tag: 'h4',
2506             cls : 'modal-title',
2507             html : this.title
2508         };
2509         
2510         if(this.specificTitle){
2511             title = this.title;
2512             
2513         };
2514         
2515         var header = [];
2516         if (this.allow_close) {
2517             header.push({
2518                 tag: 'button',
2519                 cls : 'close',
2520                 html : '&times'
2521             });
2522         }
2523         header.push(title);
2524         
2525         var modal = {
2526             cls: "modal",
2527             style : 'display: none',
2528             cn : [
2529                 {
2530                     cls: "modal-dialog",
2531                     cn : [
2532                         {
2533                             cls : "modal-content",
2534                             cn : [
2535                                 {
2536                                     cls : 'modal-header',
2537                                     cn : header
2538                                 },
2539                                 bdy,
2540                                 {
2541                                     cls : 'modal-footer',
2542                                     cn : [
2543                                         {
2544                                             tag: 'div',
2545                                             cls: 'btn-' + this.buttonPosition
2546                                         }
2547                                     ]
2548                                     
2549                                 }
2550                                 
2551                                 
2552                             ]
2553                             
2554                         }
2555                     ]
2556                         
2557                 }
2558             ]
2559         };
2560         
2561         if(this.animate){
2562             modal.cls += ' fade';
2563         }
2564         
2565         return modal;
2566           
2567     },
2568     getChildContainer : function() {
2569          
2570          return this.bodyEl;
2571         
2572     },
2573     getButtonContainer : function() {
2574          return this.el.select('.modal-footer div',true).first();
2575         
2576     },
2577     initEvents : function()
2578     {
2579         if (this.allow_close) {
2580             this.closeEl.on('click', this.hide, this);
2581         }
2582         
2583         var _this = this;
2584         
2585         window.addEventListener("resize", function() { _this.resize(); } );
2586
2587     },
2588     
2589     resize : function()
2590     {
2591         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2592     },
2593     
2594     show : function() {
2595         
2596         if (!this.rendered) {
2597             this.render();
2598         }
2599         
2600         this.el.setStyle('display', 'block');
2601         
2602         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2603             var _this = this;
2604             (function(){
2605                 this.el.addClass('in');
2606             }).defer(50, this);
2607         }else{
2608             this.el.addClass('in');
2609             
2610         }
2611         
2612         // not sure how we can show data in here.. 
2613         //if (this.tmpl) {
2614         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2615         //}
2616         
2617         Roo.get(document.body).addClass("x-body-masked");
2618         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2619         this.maskEl.show();
2620         this.el.setStyle('zIndex', '10001');
2621        
2622         this.fireEvent('show', this);
2623          
2624         
2625         
2626     },
2627     hide : function()
2628     {
2629         this.maskEl.hide();
2630         Roo.get(document.body).removeClass("x-body-masked");
2631         this.el.removeClass('in');
2632         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2633         
2634         if(this.animate){ // why
2635             var _this = this;
2636             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2637         }else{
2638             this.el.setStyle('display', 'none');
2639         }
2640         
2641         this.fireEvent('hide', this);
2642     },
2643     
2644     addButton : function(str, cb)
2645     {
2646          
2647         
2648         var b = Roo.apply({}, { html : str } );
2649         b.xns = b.xns || Roo.bootstrap;
2650         b.xtype = b.xtype || 'Button';
2651         if (typeof(b.listeners) == 'undefined') {
2652             b.listeners = { click : cb.createDelegate(this)  };
2653         }
2654         
2655         var btn = Roo.factory(b);
2656            
2657         btn.onRender(this.el.select('.modal-footer div').first());
2658         
2659         return btn;   
2660        
2661     },
2662     
2663     setDefaultButton : function(btn)
2664     {
2665         //this.el.select('.modal-footer').()
2666     },
2667     resizeTo: function(w,h)
2668     {
2669         // skip..
2670     },
2671     setContentSize  : function(w, h)
2672     {
2673         
2674     },
2675     onButtonClick: function(btn,e)
2676     {
2677         //Roo.log([a,b,c]);
2678         this.fireEvent('btnclick', btn.name, e);
2679     },
2680      /**
2681      * Set the title of the Dialog
2682      * @param {String} str new Title
2683      */
2684     setTitle: function(str) {
2685         this.titleEl.dom.innerHTML = str;    
2686     },
2687     /**
2688      * Set the body of the Dialog
2689      * @param {String} str new Title
2690      */
2691     setBody: function(str) {
2692         this.bodyEl.dom.innerHTML = str;    
2693     },
2694     /**
2695      * Set the body of the Dialog using the template
2696      * @param {Obj} data - apply this data to the template and replace the body contents.
2697      */
2698     applyBody: function(obj)
2699     {
2700         if (!this.tmpl) {
2701             Roo.log("Error - using apply Body without a template");
2702             //code
2703         }
2704         this.tmpl.overwrite(this.bodyEl, obj);
2705     }
2706     
2707 });
2708
2709
2710 Roo.apply(Roo.bootstrap.Modal,  {
2711     /**
2712          * Button config that displays a single OK button
2713          * @type Object
2714          */
2715         OK :  [{
2716             name : 'ok',
2717             weight : 'primary',
2718             html : 'OK'
2719         }], 
2720         /**
2721          * Button config that displays Yes and No buttons
2722          * @type Object
2723          */
2724         YESNO : [
2725             {
2726                 name  : 'no',
2727                 html : 'No'
2728             },
2729             {
2730                 name  :'yes',
2731                 weight : 'primary',
2732                 html : 'Yes'
2733             }
2734         ],
2735         
2736         /**
2737          * Button config that displays OK and Cancel buttons
2738          * @type Object
2739          */
2740         OKCANCEL : [
2741             {
2742                name : 'cancel',
2743                 html : 'Cancel'
2744             },
2745             {
2746                 name : 'ok',
2747                 weight : 'primary',
2748                 html : 'OK'
2749             }
2750         ],
2751         /**
2752          * Button config that displays Yes, No and Cancel buttons
2753          * @type Object
2754          */
2755         YESNOCANCEL : [
2756             {
2757                 name : 'yes',
2758                 weight : 'primary',
2759                 html : 'Yes'
2760             },
2761             {
2762                 name : 'no',
2763                 html : 'No'
2764             },
2765             {
2766                 name : 'cancel',
2767                 html : 'Cancel'
2768             }
2769         ]
2770 });
2771  
2772  /*
2773  * - LGPL
2774  *
2775  * messagebox - can be used as a replace
2776  * 
2777  */
2778 /**
2779  * @class Roo.MessageBox
2780  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2781  * Example usage:
2782  *<pre><code>
2783 // Basic alert:
2784 Roo.Msg.alert('Status', 'Changes saved successfully.');
2785
2786 // Prompt for user data:
2787 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2788     if (btn == 'ok'){
2789         // process text value...
2790     }
2791 });
2792
2793 // Show a dialog using config options:
2794 Roo.Msg.show({
2795    title:'Save Changes?',
2796    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2797    buttons: Roo.Msg.YESNOCANCEL,
2798    fn: processResult,
2799    animEl: 'elId'
2800 });
2801 </code></pre>
2802  * @singleton
2803  */
2804 Roo.bootstrap.MessageBox = function(){
2805     var dlg, opt, mask, waitTimer;
2806     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2807     var buttons, activeTextEl, bwidth;
2808
2809     
2810     // private
2811     var handleButton = function(button){
2812         dlg.hide();
2813         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2814     };
2815
2816     // private
2817     var handleHide = function(){
2818         if(opt && opt.cls){
2819             dlg.el.removeClass(opt.cls);
2820         }
2821         //if(waitTimer){
2822         //    Roo.TaskMgr.stop(waitTimer);
2823         //    waitTimer = null;
2824         //}
2825     };
2826
2827     // private
2828     var updateButtons = function(b){
2829         var width = 0;
2830         if(!b){
2831             buttons["ok"].hide();
2832             buttons["cancel"].hide();
2833             buttons["yes"].hide();
2834             buttons["no"].hide();
2835             //dlg.footer.dom.style.display = 'none';
2836             return width;
2837         }
2838         dlg.footerEl.dom.style.display = '';
2839         for(var k in buttons){
2840             if(typeof buttons[k] != "function"){
2841                 if(b[k]){
2842                     buttons[k].show();
2843                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2844                     width += buttons[k].el.getWidth()+15;
2845                 }else{
2846                     buttons[k].hide();
2847                 }
2848             }
2849         }
2850         return width;
2851     };
2852
2853     // private
2854     var handleEsc = function(d, k, e){
2855         if(opt && opt.closable !== false){
2856             dlg.hide();
2857         }
2858         if(e){
2859             e.stopEvent();
2860         }
2861     };
2862
2863     return {
2864         /**
2865          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2866          * @return {Roo.BasicDialog} The BasicDialog element
2867          */
2868         getDialog : function(){
2869            if(!dlg){
2870                 dlg = new Roo.bootstrap.Modal( {
2871                     //draggable: true,
2872                     //resizable:false,
2873                     //constraintoviewport:false,
2874                     //fixedcenter:true,
2875                     //collapsible : false,
2876                     //shim:true,
2877                     //modal: true,
2878                   //  width:400,
2879                   //  height:100,
2880                     //buttonAlign:"center",
2881                     closeClick : function(){
2882                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2883                             handleButton("no");
2884                         }else{
2885                             handleButton("cancel");
2886                         }
2887                     }
2888                 });
2889                 dlg.render();
2890                 dlg.on("hide", handleHide);
2891                 mask = dlg.mask;
2892                 //dlg.addKeyListener(27, handleEsc);
2893                 buttons = {};
2894                 this.buttons = buttons;
2895                 var bt = this.buttonText;
2896                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2897                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2898                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2899                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2900                 //Roo.log(buttons);
2901                 bodyEl = dlg.bodyEl.createChild({
2902
2903                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2904                         '<textarea class="roo-mb-textarea"></textarea>' +
2905                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2906                 });
2907                 msgEl = bodyEl.dom.firstChild;
2908                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2909                 textboxEl.enableDisplayMode();
2910                 textboxEl.addKeyListener([10,13], function(){
2911                     if(dlg.isVisible() && opt && opt.buttons){
2912                         if(opt.buttons.ok){
2913                             handleButton("ok");
2914                         }else if(opt.buttons.yes){
2915                             handleButton("yes");
2916                         }
2917                     }
2918                 });
2919                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2920                 textareaEl.enableDisplayMode();
2921                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2922                 progressEl.enableDisplayMode();
2923                 var pf = progressEl.dom.firstChild;
2924                 if (pf) {
2925                     pp = Roo.get(pf.firstChild);
2926                     pp.setHeight(pf.offsetHeight);
2927                 }
2928                 
2929             }
2930             return dlg;
2931         },
2932
2933         /**
2934          * Updates the message box body text
2935          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2936          * the XHTML-compliant non-breaking space character '&amp;#160;')
2937          * @return {Roo.MessageBox} This message box
2938          */
2939         updateText : function(text){
2940             if(!dlg.isVisible() && !opt.width){
2941                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2942             }
2943             msgEl.innerHTML = text || '&#160;';
2944       
2945             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2946             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2947             var w = Math.max(
2948                     Math.min(opt.width || cw , this.maxWidth), 
2949                     Math.max(opt.minWidth || this.minWidth, bwidth)
2950             );
2951             if(opt.prompt){
2952                 activeTextEl.setWidth(w);
2953             }
2954             if(dlg.isVisible()){
2955                 dlg.fixedcenter = false;
2956             }
2957             // to big, make it scroll. = But as usual stupid IE does not support
2958             // !important..
2959             
2960             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2961                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2962                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2963             } else {
2964                 bodyEl.dom.style.height = '';
2965                 bodyEl.dom.style.overflowY = '';
2966             }
2967             if (cw > w) {
2968                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2969             } else {
2970                 bodyEl.dom.style.overflowX = '';
2971             }
2972             
2973             dlg.setContentSize(w, bodyEl.getHeight());
2974             if(dlg.isVisible()){
2975                 dlg.fixedcenter = true;
2976             }
2977             return this;
2978         },
2979
2980         /**
2981          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
2982          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2983          * @param {Number} value Any number between 0 and 1 (e.g., .5)
2984          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2985          * @return {Roo.MessageBox} This message box
2986          */
2987         updateProgress : function(value, text){
2988             if(text){
2989                 this.updateText(text);
2990             }
2991             if (pp) { // weird bug on my firefox - for some reason this is not defined
2992                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2993             }
2994             return this;
2995         },        
2996
2997         /**
2998          * Returns true if the message box is currently displayed
2999          * @return {Boolean} True if the message box is visible, else false
3000          */
3001         isVisible : function(){
3002             return dlg && dlg.isVisible();  
3003         },
3004
3005         /**
3006          * Hides the message box if it is displayed
3007          */
3008         hide : function(){
3009             if(this.isVisible()){
3010                 dlg.hide();
3011             }  
3012         },
3013
3014         /**
3015          * Displays a new message box, or reinitializes an existing message box, based on the config options
3016          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3017          * The following config object properties are supported:
3018          * <pre>
3019 Property    Type             Description
3020 ----------  ---------------  ------------------------------------------------------------------------------------
3021 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3022                                    closes (defaults to undefined)
3023 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3024                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3025 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3026                                    progress and wait dialogs will ignore this property and always hide the
3027                                    close button as they can only be closed programmatically.
3028 cls               String           A custom CSS class to apply to the message box element
3029 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3030                                    displayed (defaults to 75)
3031 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3032                                    function will be btn (the name of the button that was clicked, if applicable,
3033                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3034                                    Progress and wait dialogs will ignore this option since they do not respond to
3035                                    user actions and can only be closed programmatically, so any required function
3036                                    should be called by the same code after it closes the dialog.
3037 icon              String           A CSS class that provides a background image to be used as an icon for
3038                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3039 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3040 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3041 modal             Boolean          False to allow user interaction with the page while the message box is
3042                                    displayed (defaults to true)
3043 msg               String           A string that will replace the existing message box body text (defaults
3044                                    to the XHTML-compliant non-breaking space character '&#160;')
3045 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3046 progress          Boolean          True to display a progress bar (defaults to false)
3047 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3048 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3049 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3050 title             String           The title text
3051 value             String           The string value to set into the active textbox element if displayed
3052 wait              Boolean          True to display a progress bar (defaults to false)
3053 width             Number           The width of the dialog in pixels
3054 </pre>
3055          *
3056          * Example usage:
3057          * <pre><code>
3058 Roo.Msg.show({
3059    title: 'Address',
3060    msg: 'Please enter your address:',
3061    width: 300,
3062    buttons: Roo.MessageBox.OKCANCEL,
3063    multiline: true,
3064    fn: saveAddress,
3065    animEl: 'addAddressBtn'
3066 });
3067 </code></pre>
3068          * @param {Object} config Configuration options
3069          * @return {Roo.MessageBox} This message box
3070          */
3071         show : function(options)
3072         {
3073             
3074             // this causes nightmares if you show one dialog after another
3075             // especially on callbacks..
3076              
3077             if(this.isVisible()){
3078                 
3079                 this.hide();
3080                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3081                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3082                 Roo.log("New Dialog Message:" +  options.msg )
3083                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3084                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3085                 
3086             }
3087             var d = this.getDialog();
3088             opt = options;
3089             d.setTitle(opt.title || "&#160;");
3090             d.closeEl.setDisplayed(opt.closable !== false);
3091             activeTextEl = textboxEl;
3092             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3093             if(opt.prompt){
3094                 if(opt.multiline){
3095                     textboxEl.hide();
3096                     textareaEl.show();
3097                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3098                         opt.multiline : this.defaultTextHeight);
3099                     activeTextEl = textareaEl;
3100                 }else{
3101                     textboxEl.show();
3102                     textareaEl.hide();
3103                 }
3104             }else{
3105                 textboxEl.hide();
3106                 textareaEl.hide();
3107             }
3108             progressEl.setDisplayed(opt.progress === true);
3109             this.updateProgress(0);
3110             activeTextEl.dom.value = opt.value || "";
3111             if(opt.prompt){
3112                 dlg.setDefaultButton(activeTextEl);
3113             }else{
3114                 var bs = opt.buttons;
3115                 var db = null;
3116                 if(bs && bs.ok){
3117                     db = buttons["ok"];
3118                 }else if(bs && bs.yes){
3119                     db = buttons["yes"];
3120                 }
3121                 dlg.setDefaultButton(db);
3122             }
3123             bwidth = updateButtons(opt.buttons);
3124             this.updateText(opt.msg);
3125             if(opt.cls){
3126                 d.el.addClass(opt.cls);
3127             }
3128             d.proxyDrag = opt.proxyDrag === true;
3129             d.modal = opt.modal !== false;
3130             d.mask = opt.modal !== false ? mask : false;
3131             if(!d.isVisible()){
3132                 // force it to the end of the z-index stack so it gets a cursor in FF
3133                 document.body.appendChild(dlg.el.dom);
3134                 d.animateTarget = null;
3135                 d.show(options.animEl);
3136             }
3137             return this;
3138         },
3139
3140         /**
3141          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3142          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3143          * and closing the message box when the process is complete.
3144          * @param {String} title The title bar text
3145          * @param {String} msg The message box body text
3146          * @return {Roo.MessageBox} This message box
3147          */
3148         progress : function(title, msg){
3149             this.show({
3150                 title : title,
3151                 msg : msg,
3152                 buttons: false,
3153                 progress:true,
3154                 closable:false,
3155                 minWidth: this.minProgressWidth,
3156                 modal : true
3157             });
3158             return this;
3159         },
3160
3161         /**
3162          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3163          * If a callback function is passed it will be called after the user clicks the button, and the
3164          * id of the button that was clicked will be passed as the only parameter to the callback
3165          * (could also be the top-right close button).
3166          * @param {String} title The title bar text
3167          * @param {String} msg The message box body text
3168          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3169          * @param {Object} scope (optional) The scope of the callback function
3170          * @return {Roo.MessageBox} This message box
3171          */
3172         alert : function(title, msg, fn, scope){
3173             this.show({
3174                 title : title,
3175                 msg : msg,
3176                 buttons: this.OK,
3177                 fn: fn,
3178                 scope : scope,
3179                 modal : true
3180             });
3181             return this;
3182         },
3183
3184         /**
3185          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3186          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3187          * You are responsible for closing the message box when the process is complete.
3188          * @param {String} msg The message box body text
3189          * @param {String} title (optional) The title bar text
3190          * @return {Roo.MessageBox} This message box
3191          */
3192         wait : function(msg, title){
3193             this.show({
3194                 title : title,
3195                 msg : msg,
3196                 buttons: false,
3197                 closable:false,
3198                 progress:true,
3199                 modal:true,
3200                 width:300,
3201                 wait:true
3202             });
3203             waitTimer = Roo.TaskMgr.start({
3204                 run: function(i){
3205                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3206                 },
3207                 interval: 1000
3208             });
3209             return this;
3210         },
3211
3212         /**
3213          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3214          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3215          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3216          * @param {String} title The title bar text
3217          * @param {String} msg The message box body text
3218          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3219          * @param {Object} scope (optional) The scope of the callback function
3220          * @return {Roo.MessageBox} This message box
3221          */
3222         confirm : function(title, msg, fn, scope){
3223             this.show({
3224                 title : title,
3225                 msg : msg,
3226                 buttons: this.YESNO,
3227                 fn: fn,
3228                 scope : scope,
3229                 modal : true
3230             });
3231             return this;
3232         },
3233
3234         /**
3235          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3236          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3237          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3238          * (could also be the top-right close button) and the text that was entered will be passed as the two
3239          * parameters to the callback.
3240          * @param {String} title The title bar text
3241          * @param {String} msg The message box body text
3242          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3243          * @param {Object} scope (optional) The scope of the callback function
3244          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3245          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3246          * @return {Roo.MessageBox} This message box
3247          */
3248         prompt : function(title, msg, fn, scope, multiline){
3249             this.show({
3250                 title : title,
3251                 msg : msg,
3252                 buttons: this.OKCANCEL,
3253                 fn: fn,
3254                 minWidth:250,
3255                 scope : scope,
3256                 prompt:true,
3257                 multiline: multiline,
3258                 modal : true
3259             });
3260             return this;
3261         },
3262
3263         /**
3264          * Button config that displays a single OK button
3265          * @type Object
3266          */
3267         OK : {ok:true},
3268         /**
3269          * Button config that displays Yes and No buttons
3270          * @type Object
3271          */
3272         YESNO : {yes:true, no:true},
3273         /**
3274          * Button config that displays OK and Cancel buttons
3275          * @type Object
3276          */
3277         OKCANCEL : {ok:true, cancel:true},
3278         /**
3279          * Button config that displays Yes, No and Cancel buttons
3280          * @type Object
3281          */
3282         YESNOCANCEL : {yes:true, no:true, cancel:true},
3283
3284         /**
3285          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3286          * @type Number
3287          */
3288         defaultTextHeight : 75,
3289         /**
3290          * The maximum width in pixels of the message box (defaults to 600)
3291          * @type Number
3292          */
3293         maxWidth : 600,
3294         /**
3295          * The minimum width in pixels of the message box (defaults to 100)
3296          * @type Number
3297          */
3298         minWidth : 100,
3299         /**
3300          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3301          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3302          * @type Number
3303          */
3304         minProgressWidth : 250,
3305         /**
3306          * An object containing the default button text strings that can be overriden for localized language support.
3307          * Supported properties are: ok, cancel, yes and no.
3308          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3309          * @type Object
3310          */
3311         buttonText : {
3312             ok : "OK",
3313             cancel : "Cancel",
3314             yes : "Yes",
3315             no : "No"
3316         }
3317     };
3318 }();
3319
3320 /**
3321  * Shorthand for {@link Roo.MessageBox}
3322  */
3323 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3324 Roo.Msg = Roo.Msg || Roo.MessageBox;
3325 /*
3326  * - LGPL
3327  *
3328  * navbar
3329  * 
3330  */
3331
3332 /**
3333  * @class Roo.bootstrap.Navbar
3334  * @extends Roo.bootstrap.Component
3335  * Bootstrap Navbar class
3336
3337  * @constructor
3338  * Create a new Navbar
3339  * @param {Object} config The config object
3340  */
3341
3342
3343 Roo.bootstrap.Navbar = function(config){
3344     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3345     
3346 };
3347
3348 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3349     
3350     
3351    
3352     // private
3353     navItems : false,
3354     loadMask : false,
3355     
3356     
3357     getAutoCreate : function(){
3358         
3359         
3360         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3361         
3362     },
3363     
3364     initEvents :function ()
3365     {
3366         //Roo.log(this.el.select('.navbar-toggle',true));
3367         this.el.select('.navbar-toggle',true).on('click', function() {
3368            // Roo.log('click');
3369             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3370         }, this);
3371         
3372         var mark = {
3373             tag: "div",
3374             cls:"x-dlg-mask"
3375         };
3376         
3377         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3378         
3379         var size = this.el.getSize();
3380         this.maskEl.setSize(size.width, size.height);
3381         this.maskEl.enableDisplayMode("block");
3382         this.maskEl.hide();
3383         
3384         if(this.loadMask){
3385             this.maskEl.show();
3386         }
3387     },
3388     
3389     
3390     getChildContainer : function()
3391     {
3392         if (this.el.select('.collapse').getCount()) {
3393             return this.el.select('.collapse',true).first();
3394         }
3395         
3396         return this.el;
3397     },
3398     
3399     mask : function()
3400     {
3401         this.maskEl.show();
3402     },
3403     
3404     unmask : function()
3405     {
3406         this.maskEl.hide();
3407     } 
3408     
3409     
3410     
3411     
3412 });
3413
3414
3415
3416  
3417
3418  /*
3419  * - LGPL
3420  *
3421  * navbar
3422  * 
3423  */
3424
3425 /**
3426  * @class Roo.bootstrap.NavSimplebar
3427  * @extends Roo.bootstrap.Navbar
3428  * Bootstrap Sidebar class
3429  *
3430  * @cfg {Boolean} inverse is inverted color
3431  * 
3432  * @cfg {String} type (nav | pills | tabs)
3433  * @cfg {Boolean} arrangement stacked | justified
3434  * @cfg {String} align (left | right) alignment
3435  * 
3436  * @cfg {Boolean} main (true|false) main nav bar? default false
3437  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3438  * 
3439  * @cfg {String} tag (header|footer|nav|div) default is nav 
3440
3441  * 
3442  * 
3443  * 
3444  * @constructor
3445  * Create a new Sidebar
3446  * @param {Object} config The config object
3447  */
3448
3449
3450 Roo.bootstrap.NavSimplebar = function(config){
3451     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3452 };
3453
3454 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3455     
3456     inverse: false,
3457     
3458     type: false,
3459     arrangement: '',
3460     align : false,
3461     
3462     
3463     
3464     main : false,
3465     
3466     
3467     tag : false,
3468     
3469     
3470     getAutoCreate : function(){
3471         
3472         
3473         var cfg = {
3474             tag : this.tag || 'div',
3475             cls : 'navbar'
3476         };
3477           
3478         
3479         cfg.cn = [
3480             {
3481                 cls: 'nav',
3482                 tag : 'ul'
3483             }
3484         ];
3485         
3486          
3487         this.type = this.type || 'nav';
3488         if (['tabs','pills'].indexOf(this.type)!==-1) {
3489             cfg.cn[0].cls += ' nav-' + this.type
3490         
3491         
3492         } else {
3493             if (this.type!=='nav') {
3494                 Roo.log('nav type must be nav/tabs/pills')
3495             }
3496             cfg.cn[0].cls += ' navbar-nav'
3497         }
3498         
3499         
3500         
3501         
3502         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3503             cfg.cn[0].cls += ' nav-' + this.arrangement;
3504         }
3505         
3506         
3507         if (this.align === 'right') {
3508             cfg.cn[0].cls += ' navbar-right';
3509         }
3510         
3511         if (this.inverse) {
3512             cfg.cls += ' navbar-inverse';
3513             
3514         }
3515         
3516         
3517         return cfg;
3518     
3519         
3520     }
3521     
3522     
3523     
3524 });
3525
3526
3527
3528  
3529
3530  
3531        /*
3532  * - LGPL
3533  *
3534  * navbar
3535  * 
3536  */
3537
3538 /**
3539  * @class Roo.bootstrap.NavHeaderbar
3540  * @extends Roo.bootstrap.NavSimplebar
3541  * Bootstrap Sidebar class
3542  *
3543  * @cfg {String} brand what is brand
3544  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3545  * @cfg {String} brand_href href of the brand
3546  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3547  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3548  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3549  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3550  * 
3551  * @constructor
3552  * Create a new Sidebar
3553  * @param {Object} config The config object
3554  */
3555
3556
3557 Roo.bootstrap.NavHeaderbar = function(config){
3558     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3559       
3560 };
3561
3562 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3563     
3564     position: '',
3565     brand: '',
3566     brand_href: false,
3567     srButton : true,
3568     autohide : false,
3569     desktopCenter : false,
3570    
3571     
3572     getAutoCreate : function(){
3573         
3574         var   cfg = {
3575             tag: this.nav || 'nav',
3576             cls: 'navbar',
3577             role: 'navigation',
3578             cn: []
3579         };
3580         
3581         var cn = cfg.cn;
3582         if (this.desktopCenter) {
3583             cn.push({cls : 'container', cn : []});
3584             cn = cn[0].cn;
3585         }
3586         
3587         if(this.srButton){
3588             cn.push({
3589                 tag: 'div',
3590                 cls: 'navbar-header',
3591                 cn: [
3592                     {
3593                         tag: 'button',
3594                         type: 'button',
3595                         cls: 'navbar-toggle',
3596                         'data-toggle': 'collapse',
3597                         cn: [
3598                             {
3599                                 tag: 'span',
3600                                 cls: 'sr-only',
3601                                 html: 'Toggle navigation'
3602                             },
3603                             {
3604                                 tag: 'span',
3605                                 cls: 'icon-bar'
3606                             },
3607                             {
3608                                 tag: 'span',
3609                                 cls: 'icon-bar'
3610                             },
3611                             {
3612                                 tag: 'span',
3613                                 cls: 'icon-bar'
3614                             }
3615                         ]
3616                     }
3617                 ]
3618             });
3619         }
3620         
3621         cn.push({
3622             tag: 'div',
3623             cls: 'collapse navbar-collapse',
3624             cn : []
3625         });
3626         
3627         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3628         
3629         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3630             cfg.cls += ' navbar-' + this.position;
3631             
3632             // tag can override this..
3633             
3634             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3635         }
3636         
3637         if (this.brand !== '') {
3638             cn[0].cn.push({
3639                 tag: 'a',
3640                 href: this.brand_href ? this.brand_href : '#',
3641                 cls: 'navbar-brand',
3642                 cn: [
3643                 this.brand
3644                 ]
3645             });
3646         }
3647         
3648         if(this.main){
3649             cfg.cls += ' main-nav';
3650         }
3651         
3652         
3653         return cfg;
3654
3655         
3656     },
3657     getHeaderChildContainer : function()
3658     {
3659         if (this.el.select('.navbar-header').getCount()) {
3660             return this.el.select('.navbar-header',true).first();
3661         }
3662         
3663         return this.getChildContainer();
3664     },
3665     
3666     
3667     initEvents : function()
3668     {
3669         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3670         
3671         if (this.autohide) {
3672             
3673             var prevScroll = 0;
3674             var ft = this.el;
3675             
3676             Roo.get(document).on('scroll',function(e) {
3677                 var ns = Roo.get(document).getScroll().top;
3678                 var os = prevScroll;
3679                 prevScroll = ns;
3680                 
3681                 if(ns > os){
3682                     ft.removeClass('slideDown');
3683                     ft.addClass('slideUp');
3684                     return;
3685                 }
3686                 ft.removeClass('slideUp');
3687                 ft.addClass('slideDown');
3688                  
3689               
3690           },this);
3691         }
3692     }    
3693     
3694 });
3695
3696
3697
3698  
3699
3700  /*
3701  * - LGPL
3702  *
3703  * navbar
3704  * 
3705  */
3706
3707 /**
3708  * @class Roo.bootstrap.NavSidebar
3709  * @extends Roo.bootstrap.Navbar
3710  * Bootstrap Sidebar class
3711  * 
3712  * @constructor
3713  * Create a new Sidebar
3714  * @param {Object} config The config object
3715  */
3716
3717
3718 Roo.bootstrap.NavSidebar = function(config){
3719     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3720 };
3721
3722 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3723     
3724     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3725     
3726     getAutoCreate : function(){
3727         
3728         
3729         return  {
3730             tag: 'div',
3731             cls: 'sidebar sidebar-nav'
3732         };
3733     
3734         
3735     }
3736     
3737     
3738     
3739 });
3740
3741
3742
3743  
3744
3745  /*
3746  * - LGPL
3747  *
3748  * nav group
3749  * 
3750  */
3751
3752 /**
3753  * @class Roo.bootstrap.NavGroup
3754  * @extends Roo.bootstrap.Component
3755  * Bootstrap NavGroup class
3756  * @cfg {String} align (left|right)
3757  * @cfg {Boolean} inverse
3758  * @cfg {String} type (nav|pills|tab) default nav
3759  * @cfg {String} navId - reference Id for navbar.
3760
3761  * 
3762  * @constructor
3763  * Create a new nav group
3764  * @param {Object} config The config object
3765  */
3766
3767 Roo.bootstrap.NavGroup = function(config){
3768     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3769     this.navItems = [];
3770    
3771     Roo.bootstrap.NavGroup.register(this);
3772      this.addEvents({
3773         /**
3774              * @event changed
3775              * Fires when the active item changes
3776              * @param {Roo.bootstrap.NavGroup} this
3777              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3778              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3779          */
3780         'changed': true
3781      });
3782     
3783 };
3784
3785 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3786     
3787     align: '',
3788     inverse: false,
3789     form: false,
3790     type: 'nav',
3791     navId : '',
3792     // private
3793     
3794     navItems : false, 
3795     
3796     getAutoCreate : function()
3797     {
3798         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3799         
3800         cfg = {
3801             tag : 'ul',
3802             cls: 'nav' 
3803         };
3804         
3805         if (['tabs','pills'].indexOf(this.type)!==-1) {
3806             cfg.cls += ' nav-' + this.type
3807         } else {
3808             if (this.type!=='nav') {
3809                 Roo.log('nav type must be nav/tabs/pills')
3810             }
3811             cfg.cls += ' navbar-nav'
3812         }
3813         
3814         if (this.parent().sidebar) {
3815             cfg = {
3816                 tag: 'ul',
3817                 cls: 'dashboard-menu sidebar-menu'
3818             };
3819             
3820             return cfg;
3821         }
3822         
3823         if (this.form === true) {
3824             cfg = {
3825                 tag: 'form',
3826                 cls: 'navbar-form'
3827             };
3828             
3829             if (this.align === 'right') {
3830                 cfg.cls += ' navbar-right';
3831             } else {
3832                 cfg.cls += ' navbar-left';
3833             }
3834         }
3835         
3836         if (this.align === 'right') {
3837             cfg.cls += ' navbar-right';
3838         }
3839         
3840         if (this.inverse) {
3841             cfg.cls += ' navbar-inverse';
3842             
3843         }
3844         
3845         
3846         return cfg;
3847     },
3848     /**
3849     * sets the active Navigation item
3850     * @param {Roo.bootstrap.NavItem} the new current navitem
3851     */
3852     setActiveItem : function(item)
3853     {
3854         var prev = false;
3855         Roo.each(this.navItems, function(v){
3856             if (v == item) {
3857                 return ;
3858             }
3859             if (v.isActive()) {
3860                 v.setActive(false, true);
3861                 prev = v;
3862                 
3863             }
3864             
3865         });
3866
3867         item.setActive(true, true);
3868         this.fireEvent('changed', this, item, prev);
3869         
3870         
3871     },
3872     /**
3873     * gets the active Navigation item
3874     * @return {Roo.bootstrap.NavItem} the current navitem
3875     */
3876     getActive : function()
3877     {
3878         
3879         var prev = false;
3880         Roo.each(this.navItems, function(v){
3881             
3882             if (v.isActive()) {
3883                 prev = v;
3884                 
3885             }
3886             
3887         });
3888         return prev;
3889     },
3890     
3891     indexOfNav : function()
3892     {
3893         
3894         var prev = false;
3895         Roo.each(this.navItems, function(v,i){
3896             
3897             if (v.isActive()) {
3898                 prev = i;
3899                 
3900             }
3901             
3902         });
3903         return prev;
3904     },
3905     /**
3906     * adds a Navigation item
3907     * @param {Roo.bootstrap.NavItem} the navitem to add
3908     */
3909     addItem : function(cfg)
3910     {
3911         var cn = new Roo.bootstrap.NavItem(cfg);
3912         this.register(cn);
3913         cn.parentId = this.id;
3914         cn.onRender(this.el, null);
3915         return cn;
3916     },
3917     /**
3918     * register a Navigation item
3919     * @param {Roo.bootstrap.NavItem} the navitem to add
3920     */
3921     register : function(item)
3922     {
3923         this.navItems.push( item);
3924         item.navId = this.navId;
3925     
3926     },
3927     
3928     /**
3929     * clear all the Navigation item
3930     */
3931    
3932     clearAll : function()
3933     {
3934         this.navItems = [];
3935         this.el.dom.innerHTML = '';
3936     },
3937     
3938     getNavItem: function(tabId)
3939     {
3940         var ret = false;
3941         Roo.each(this.navItems, function(e) {
3942             if (e.tabId == tabId) {
3943                ret =  e;
3944                return false;
3945             }
3946             return true;
3947             
3948         });
3949         return ret;
3950     },
3951     
3952     setActiveNext : function()
3953     {
3954         var i = this.indexOfNav(this.getActive());
3955         if (i > this.navItems.length) {
3956             return;
3957         }
3958         this.setActiveItem(this.navItems[i+1]);
3959     },
3960     setActivePrev : function()
3961     {
3962         var i = this.indexOfNav(this.getActive());
3963         if (i  < 1) {
3964             return;
3965         }
3966         this.setActiveItem(this.navItems[i-1]);
3967     },
3968     clearWasActive : function(except) {
3969         Roo.each(this.navItems, function(e) {
3970             if (e.tabId != except.tabId && e.was_active) {
3971                e.was_active = false;
3972                return false;
3973             }
3974             return true;
3975             
3976         });
3977     },
3978     getWasActive : function ()
3979     {
3980         var r = false;
3981         Roo.each(this.navItems, function(e) {
3982             if (e.was_active) {
3983                r = e;
3984                return false;
3985             }
3986             return true;
3987             
3988         });
3989         return r;
3990     }
3991     
3992     
3993 });
3994
3995  
3996 Roo.apply(Roo.bootstrap.NavGroup, {
3997     
3998     groups: {},
3999      /**
4000     * register a Navigation Group
4001     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4002     */
4003     register : function(navgrp)
4004     {
4005         this.groups[navgrp.navId] = navgrp;
4006         
4007     },
4008     /**
4009     * fetch a Navigation Group based on the navigation ID
4010     * @param {string} the navgroup to add
4011     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4012     */
4013     get: function(navId) {
4014         if (typeof(this.groups[navId]) == 'undefined') {
4015             return false;
4016             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4017         }
4018         return this.groups[navId] ;
4019     }
4020     
4021     
4022     
4023 });
4024
4025  /*
4026  * - LGPL
4027  *
4028  * row
4029  * 
4030  */
4031
4032 /**
4033  * @class Roo.bootstrap.NavItem
4034  * @extends Roo.bootstrap.Component
4035  * Bootstrap Navbar.NavItem class
4036  * @cfg {String} href  link to
4037  * @cfg {String} html content of button
4038  * @cfg {String} badge text inside badge
4039  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4040  * @cfg {String} glyphicon name of glyphicon
4041  * @cfg {String} icon name of font awesome icon
4042  * @cfg {Boolean} active Is item active
4043  * @cfg {Boolean} disabled Is item disabled
4044  
4045  * @cfg {Boolean} preventDefault (true | false) default false
4046  * @cfg {String} tabId the tab that this item activates.
4047  * @cfg {String} tagtype (a|span) render as a href or span?
4048  * @cfg {Boolean} animateRef (true|false) link to element default false  
4049   
4050  * @constructor
4051  * Create a new Navbar Item
4052  * @param {Object} config The config object
4053  */
4054 Roo.bootstrap.NavItem = function(config){
4055     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4056     this.addEvents({
4057         // raw events
4058         /**
4059          * @event click
4060          * The raw click event for the entire grid.
4061          * @param {Roo.EventObject} e
4062          */
4063         "click" : true,
4064          /**
4065             * @event changed
4066             * Fires when the active item active state changes
4067             * @param {Roo.bootstrap.NavItem} this
4068             * @param {boolean} state the new state
4069              
4070          */
4071         'changed': true,
4072         /**
4073             * @event scrollto
4074             * Fires when scroll to element
4075             * @param {Roo.bootstrap.NavItem} this
4076             * @param {Object} options
4077             * @param {Roo.EventObject} e
4078              
4079          */
4080         'scrollto': true
4081     });
4082    
4083 };
4084
4085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4086     
4087     href: false,
4088     html: '',
4089     badge: '',
4090     icon: false,
4091     glyphicon: false,
4092     active: false,
4093     preventDefault : false,
4094     tabId : false,
4095     tagtype : 'a',
4096     disabled : false,
4097     animateRef : false,
4098     was_active : false,
4099     
4100     getAutoCreate : function(){
4101          
4102         var cfg = {
4103             tag: 'li',
4104             cls: 'nav-item'
4105             
4106         };
4107         
4108         if (this.active) {
4109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4110         }
4111         if (this.disabled) {
4112             cfg.cls += ' disabled';
4113         }
4114         
4115         if (this.href || this.html || this.glyphicon || this.icon) {
4116             cfg.cn = [
4117                 {
4118                     tag: this.tagtype,
4119                     href : this.href || "#",
4120                     html: this.html || ''
4121                 }
4122             ];
4123             
4124             if (this.icon) {
4125                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4126             }
4127
4128             if(this.glyphicon) {
4129                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4130             }
4131             
4132             if (this.menu) {
4133                 
4134                 cfg.cn[0].html += " <span class='caret'></span>";
4135              
4136             }
4137             
4138             if (this.badge !== '') {
4139                  
4140                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4141             }
4142         }
4143         
4144         
4145         
4146         return cfg;
4147     },
4148     initEvents: function() 
4149     {
4150         if (typeof (this.menu) != 'undefined') {
4151             this.menu.parentType = this.xtype;
4152             this.menu.triggerEl = this.el;
4153             this.menu = this.addxtype(Roo.apply({}, this.menu));
4154         }
4155         
4156         this.el.select('a',true).on('click', this.onClick, this);
4157         
4158         if(this.tagtype == 'span'){
4159             this.el.select('span',true).on('click', this.onClick, this);
4160         }
4161        
4162         // at this point parent should be available..
4163         this.parent().register(this);
4164     },
4165     
4166     onClick : function(e)
4167     {
4168         if(
4169                 this.preventDefault || 
4170                 this.href == '#' 
4171         ){
4172             
4173             e.preventDefault();
4174         }
4175         
4176         if (this.disabled) {
4177             return;
4178         }
4179         
4180         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4181         if (tg && tg.transition) {
4182             Roo.log("waiting for the transitionend");
4183             return;
4184         }
4185         
4186         
4187         
4188         //Roo.log("fire event clicked");
4189         if(this.fireEvent('click', this, e) === false){
4190             return;
4191         };
4192         
4193         if(this.tagtype == 'span'){
4194             return;
4195         }
4196         
4197         //Roo.log(this.href);
4198         var ael = this.el.select('a',true).first();
4199         //Roo.log(ael);
4200         
4201         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4202             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4203             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4204                 return; // ignore... - it's a 'hash' to another page.
4205             }
4206             
4207             e.preventDefault();
4208             this.scrollToElement(e);
4209         }
4210         
4211         
4212         var p =  this.parent();
4213    
4214         if (['tabs','pills'].indexOf(p.type)!==-1) {
4215             if (typeof(p.setActiveItem) !== 'undefined') {
4216                 p.setActiveItem(this);
4217             }
4218         }
4219         
4220         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4221         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4222             // remove the collapsed menu expand...
4223             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4224         }
4225     },
4226     
4227     isActive: function () {
4228         return this.active
4229     },
4230     setActive : function(state, fire, is_was_active)
4231     {
4232         if (this.active && !state && this.navId) {
4233             this.was_active = true;
4234             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4235             if (nv) {
4236                 nv.clearWasActive(this);
4237             }
4238             
4239         }
4240         this.active = state;
4241         
4242         if (!state ) {
4243             this.el.removeClass('active');
4244         } else if (!this.el.hasClass('active')) {
4245             this.el.addClass('active');
4246         }
4247         if (fire) {
4248             this.fireEvent('changed', this, state);
4249         }
4250         
4251         // show a panel if it's registered and related..
4252         
4253         if (!this.navId || !this.tabId || !state || is_was_active) {
4254             return;
4255         }
4256         
4257         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4258         if (!tg) {
4259             return;
4260         }
4261         var pan = tg.getPanelByName(this.tabId);
4262         if (!pan) {
4263             return;
4264         }
4265         // if we can not flip to new panel - go back to old nav highlight..
4266         if (false == tg.showPanel(pan)) {
4267             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4268             if (nv) {
4269                 var onav = nv.getWasActive();
4270                 if (onav) {
4271                     onav.setActive(true, false, true);
4272                 }
4273             }
4274             
4275         }
4276         
4277         
4278         
4279     },
4280      // this should not be here...
4281     setDisabled : function(state)
4282     {
4283         this.disabled = state;
4284         if (!state ) {
4285             this.el.removeClass('disabled');
4286         } else if (!this.el.hasClass('disabled')) {
4287             this.el.addClass('disabled');
4288         }
4289         
4290     },
4291     
4292     /**
4293      * Fetch the element to display the tooltip on.
4294      * @return {Roo.Element} defaults to this.el
4295      */
4296     tooltipEl : function()
4297     {
4298         return this.el.select('' + this.tagtype + '', true).first();
4299     },
4300     
4301     scrollToElement : function(e)
4302     {
4303         var c = document.body;
4304         
4305         /*
4306          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4307          */
4308         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4309             c = document.documentElement;
4310         }
4311         
4312         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4313         
4314         if(!target){
4315             return;
4316         }
4317
4318         var o = target.calcOffsetsTo(c);
4319         
4320         var options = {
4321             target : target,
4322             value : o[1]
4323         };
4324         
4325         this.fireEvent('scrollto', this, options, e);
4326         
4327         Roo.get(c).scrollTo('top', options.value, true);
4328         
4329         return;
4330     }
4331 });
4332  
4333
4334  /*
4335  * - LGPL
4336  *
4337  * sidebar item
4338  *
4339  *  li
4340  *    <span> icon </span>
4341  *    <span> text </span>
4342  *    <span>badge </span>
4343  */
4344
4345 /**
4346  * @class Roo.bootstrap.NavSidebarItem
4347  * @extends Roo.bootstrap.NavItem
4348  * Bootstrap Navbar.NavSidebarItem class
4349  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4350  * @constructor
4351  * Create a new Navbar Button
4352  * @param {Object} config The config object
4353  */
4354 Roo.bootstrap.NavSidebarItem = function(config){
4355     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4356     this.addEvents({
4357         // raw events
4358         /**
4359          * @event click
4360          * The raw click event for the entire grid.
4361          * @param {Roo.EventObject} e
4362          */
4363         "click" : true,
4364          /**
4365             * @event changed
4366             * Fires when the active item active state changes
4367             * @param {Roo.bootstrap.NavSidebarItem} this
4368             * @param {boolean} state the new state
4369              
4370          */
4371         'changed': true
4372     });
4373    
4374 };
4375
4376 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4377     
4378     badgeWeight : 'default',
4379     
4380     getAutoCreate : function(){
4381         
4382         
4383         var a = {
4384                 tag: 'a',
4385                 href : this.href || '#',
4386                 cls: '',
4387                 html : '',
4388                 cn : []
4389         };
4390         var cfg = {
4391             tag: 'li',
4392             cls: '',
4393             cn: [ a ]
4394         };
4395         var span = {
4396             tag: 'span',
4397             html : this.html || ''
4398         };
4399         
4400         
4401         if (this.active) {
4402             cfg.cls += ' active';
4403         }
4404         
4405         if (this.disabled) {
4406             cfg.cls += ' disabled';
4407         }
4408         
4409         // left icon..
4410         if (this.glyphicon || this.icon) {
4411             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4412             a.cn.push({ tag : 'i', cls : c }) ;
4413         }
4414         // html..
4415         a.cn.push(span);
4416         // then badge..
4417         if (this.badge !== '') {
4418             
4419             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4420         }
4421         // fi
4422         if (this.menu) {
4423             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4424             a.cls += 'dropdown-toggle treeview' ;
4425             
4426         }
4427         
4428         
4429         
4430         return cfg;
4431          
4432            
4433     },
4434     
4435     initEvents : function()
4436     { 
4437         this.el.on('click', this.onClick, this);
4438        
4439     
4440         if(this.badge !== ''){
4441  
4442             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4443         }
4444         
4445     },
4446     
4447     onClick : function(e)
4448     {
4449         if(this.disabled){
4450             e.preventDefault();
4451             return;
4452         }
4453         
4454         if(this.preventDefault){
4455             e.preventDefault();
4456         }
4457         
4458         this.fireEvent('click', this);
4459     },
4460     
4461     disable : function()
4462     {
4463         this.setDisabled(true);
4464     },
4465     
4466     enable : function()
4467     {
4468         this.setDisabled(false);
4469     },
4470     
4471     setDisabled : function(state)
4472     {
4473         if(this.disabled == state){
4474             return;
4475         }
4476         
4477         this.disabled = state;
4478         
4479         if (state) {
4480             this.el.addClass('disabled');
4481             return;
4482         }
4483         
4484         this.el.removeClass('disabled');
4485         
4486         return;
4487     },
4488     
4489     setActive : function(state)
4490     {
4491         if(this.active == state){
4492             return;
4493         }
4494         
4495         this.active = state;
4496         
4497         if (state) {
4498             this.el.addClass('active');
4499             return;
4500         }
4501         
4502         this.el.removeClass('active');
4503         
4504         return;
4505     },
4506     
4507     isActive: function () 
4508     {
4509         return this.active;
4510     },
4511     
4512     setBadge : function(str)
4513     {
4514         if(!this.badgeEl){
4515             return;
4516         }
4517         
4518         this.badgeEl.dom.innerHTML = str;
4519     }
4520     
4521    
4522      
4523  
4524 });
4525  
4526
4527  /*
4528  * - LGPL
4529  *
4530  * row
4531  * 
4532  */
4533
4534 /**
4535  * @class Roo.bootstrap.Row
4536  * @extends Roo.bootstrap.Component
4537  * Bootstrap Row class (contains columns...)
4538  * 
4539  * @constructor
4540  * Create a new Row
4541  * @param {Object} config The config object
4542  */
4543
4544 Roo.bootstrap.Row = function(config){
4545     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4546 };
4547
4548 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4549     
4550     getAutoCreate : function(){
4551        return {
4552             cls: 'row clearfix'
4553        };
4554     }
4555     
4556     
4557 });
4558
4559  
4560
4561  /*
4562  * - LGPL
4563  *
4564  * element
4565  * 
4566  */
4567
4568 /**
4569  * @class Roo.bootstrap.Element
4570  * @extends Roo.bootstrap.Component
4571  * Bootstrap Element class
4572  * @cfg {String} html contents of the element
4573  * @cfg {String} tag tag of the element
4574  * @cfg {String} cls class of the element
4575  * @cfg {Boolean} preventDefault (true|false) default false
4576  * @cfg {Boolean} clickable (true|false) default false
4577  * 
4578  * @constructor
4579  * Create a new Element
4580  * @param {Object} config The config object
4581  */
4582
4583 Roo.bootstrap.Element = function(config){
4584     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4585     
4586     this.addEvents({
4587         // raw events
4588         /**
4589          * @event click
4590          * When a element is chick
4591          * @param {Roo.bootstrap.Element} this
4592          * @param {Roo.EventObject} e
4593          */
4594         "click" : true
4595     });
4596 };
4597
4598 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4599     
4600     tag: 'div',
4601     cls: '',
4602     html: '',
4603     preventDefault: false, 
4604     clickable: false,
4605     
4606     getAutoCreate : function(){
4607         
4608         var cfg = {
4609             tag: this.tag,
4610             cls: this.cls,
4611             html: this.html
4612         };
4613         
4614         return cfg;
4615     },
4616     
4617     initEvents: function() 
4618     {
4619         Roo.bootstrap.Element.superclass.initEvents.call(this);
4620         
4621         if(this.clickable){
4622             this.el.on('click', this.onClick, this);
4623         }
4624         
4625     },
4626     
4627     onClick : function(e)
4628     {
4629         if(this.preventDefault){
4630             e.preventDefault();
4631         }
4632         
4633         this.fireEvent('click', this, e);
4634     },
4635     
4636     getValue : function()
4637     {
4638         return this.el.dom.innerHTML;
4639     },
4640     
4641     setValue : function(value)
4642     {
4643         this.el.dom.innerHTML = value;
4644     }
4645    
4646 });
4647
4648  
4649
4650  /*
4651  * - LGPL
4652  *
4653  * pagination
4654  * 
4655  */
4656
4657 /**
4658  * @class Roo.bootstrap.Pagination
4659  * @extends Roo.bootstrap.Component
4660  * Bootstrap Pagination class
4661  * @cfg {String} size xs | sm | md | lg
4662  * @cfg {Boolean} inverse false | true
4663  * 
4664  * @constructor
4665  * Create a new Pagination
4666  * @param {Object} config The config object
4667  */
4668
4669 Roo.bootstrap.Pagination = function(config){
4670     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4671 };
4672
4673 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4674     
4675     cls: false,
4676     size: false,
4677     inverse: false,
4678     
4679     getAutoCreate : function(){
4680         var cfg = {
4681             tag: 'ul',
4682                 cls: 'pagination'
4683         };
4684         if (this.inverse) {
4685             cfg.cls += ' inverse';
4686         }
4687         if (this.html) {
4688             cfg.html=this.html;
4689         }
4690         if (this.cls) {
4691             cfg.cls += " " + this.cls;
4692         }
4693         return cfg;
4694     }
4695    
4696 });
4697
4698  
4699
4700  /*
4701  * - LGPL
4702  *
4703  * Pagination item
4704  * 
4705  */
4706
4707
4708 /**
4709  * @class Roo.bootstrap.PaginationItem
4710  * @extends Roo.bootstrap.Component
4711  * Bootstrap PaginationItem class
4712  * @cfg {String} html text
4713  * @cfg {String} href the link
4714  * @cfg {Boolean} preventDefault (true | false) default true
4715  * @cfg {Boolean} active (true | false) default false
4716  * @cfg {Boolean} disabled default false
4717  * 
4718  * 
4719  * @constructor
4720  * Create a new PaginationItem
4721  * @param {Object} config The config object
4722  */
4723
4724
4725 Roo.bootstrap.PaginationItem = function(config){
4726     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4727     this.addEvents({
4728         // raw events
4729         /**
4730          * @event click
4731          * The raw click event for the entire grid.
4732          * @param {Roo.EventObject} e
4733          */
4734         "click" : true
4735     });
4736 };
4737
4738 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4739     
4740     href : false,
4741     html : false,
4742     preventDefault: true,
4743     active : false,
4744     cls : false,
4745     disabled: false,
4746     
4747     getAutoCreate : function(){
4748         var cfg= {
4749             tag: 'li',
4750             cn: [
4751                 {
4752                     tag : 'a',
4753                     href : this.href ? this.href : '#',
4754                     html : this.html ? this.html : ''
4755                 }
4756             ]
4757         };
4758         
4759         if(this.cls){
4760             cfg.cls = this.cls;
4761         }
4762         
4763         if(this.disabled){
4764             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4765         }
4766         
4767         if(this.active){
4768             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4769         }
4770         
4771         return cfg;
4772     },
4773     
4774     initEvents: function() {
4775         
4776         this.el.on('click', this.onClick, this);
4777         
4778     },
4779     onClick : function(e)
4780     {
4781         Roo.log('PaginationItem on click ');
4782         if(this.preventDefault){
4783             e.preventDefault();
4784         }
4785         
4786         if(this.disabled){
4787             return;
4788         }
4789         
4790         this.fireEvent('click', this, e);
4791     }
4792    
4793 });
4794
4795  
4796
4797  /*
4798  * - LGPL
4799  *
4800  * slider
4801  * 
4802  */
4803
4804
4805 /**
4806  * @class Roo.bootstrap.Slider
4807  * @extends Roo.bootstrap.Component
4808  * Bootstrap Slider class
4809  *    
4810  * @constructor
4811  * Create a new Slider
4812  * @param {Object} config The config object
4813  */
4814
4815 Roo.bootstrap.Slider = function(config){
4816     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4817 };
4818
4819 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4820     
4821     getAutoCreate : function(){
4822         
4823         var cfg = {
4824             tag: 'div',
4825             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4826             cn: [
4827                 {
4828                     tag: 'a',
4829                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4830                 }
4831             ]
4832         };
4833         
4834         return cfg;
4835     }
4836    
4837 });
4838
4839  /*
4840  * Based on:
4841  * Ext JS Library 1.1.1
4842  * Copyright(c) 2006-2007, Ext JS, LLC.
4843  *
4844  * Originally Released Under LGPL - original licence link has changed is not relivant.
4845  *
4846  * Fork - LGPL
4847  * <script type="text/javascript">
4848  */
4849  
4850
4851 /**
4852  * @class Roo.grid.ColumnModel
4853  * @extends Roo.util.Observable
4854  * This is the default implementation of a ColumnModel used by the Grid. It defines
4855  * the columns in the grid.
4856  * <br>Usage:<br>
4857  <pre><code>
4858  var colModel = new Roo.grid.ColumnModel([
4859         {header: "Ticker", width: 60, sortable: true, locked: true},
4860         {header: "Company Name", width: 150, sortable: true},
4861         {header: "Market Cap.", width: 100, sortable: true},
4862         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4863         {header: "Employees", width: 100, sortable: true, resizable: false}
4864  ]);
4865  </code></pre>
4866  * <p>
4867  
4868  * The config options listed for this class are options which may appear in each
4869  * individual column definition.
4870  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4871  * @constructor
4872  * @param {Object} config An Array of column config objects. See this class's
4873  * config objects for details.
4874 */
4875 Roo.grid.ColumnModel = function(config){
4876         /**
4877      * The config passed into the constructor
4878      */
4879     this.config = config;
4880     this.lookup = {};
4881
4882     // if no id, create one
4883     // if the column does not have a dataIndex mapping,
4884     // map it to the order it is in the config
4885     for(var i = 0, len = config.length; i < len; i++){
4886         var c = config[i];
4887         if(typeof c.dataIndex == "undefined"){
4888             c.dataIndex = i;
4889         }
4890         if(typeof c.renderer == "string"){
4891             c.renderer = Roo.util.Format[c.renderer];
4892         }
4893         if(typeof c.id == "undefined"){
4894             c.id = Roo.id();
4895         }
4896         if(c.editor && c.editor.xtype){
4897             c.editor  = Roo.factory(c.editor, Roo.grid);
4898         }
4899         if(c.editor && c.editor.isFormField){
4900             c.editor = new Roo.grid.GridEditor(c.editor);
4901         }
4902         this.lookup[c.id] = c;
4903     }
4904
4905     /**
4906      * The width of columns which have no width specified (defaults to 100)
4907      * @type Number
4908      */
4909     this.defaultWidth = 100;
4910
4911     /**
4912      * Default sortable of columns which have no sortable specified (defaults to false)
4913      * @type Boolean
4914      */
4915     this.defaultSortable = false;
4916
4917     this.addEvents({
4918         /**
4919              * @event widthchange
4920              * Fires when the width of a column changes.
4921              * @param {ColumnModel} this
4922              * @param {Number} columnIndex The column index
4923              * @param {Number} newWidth The new width
4924              */
4925             "widthchange": true,
4926         /**
4927              * @event headerchange
4928              * Fires when the text of a header changes.
4929              * @param {ColumnModel} this
4930              * @param {Number} columnIndex The column index
4931              * @param {Number} newText The new header text
4932              */
4933             "headerchange": true,
4934         /**
4935              * @event hiddenchange
4936              * Fires when a column is hidden or "unhidden".
4937              * @param {ColumnModel} this
4938              * @param {Number} columnIndex The column index
4939              * @param {Boolean} hidden true if hidden, false otherwise
4940              */
4941             "hiddenchange": true,
4942             /**
4943          * @event columnmoved
4944          * Fires when a column is moved.
4945          * @param {ColumnModel} this
4946          * @param {Number} oldIndex
4947          * @param {Number} newIndex
4948          */
4949         "columnmoved" : true,
4950         /**
4951          * @event columlockchange
4952          * Fires when a column's locked state is changed
4953          * @param {ColumnModel} this
4954          * @param {Number} colIndex
4955          * @param {Boolean} locked true if locked
4956          */
4957         "columnlockchange" : true
4958     });
4959     Roo.grid.ColumnModel.superclass.constructor.call(this);
4960 };
4961 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4962     /**
4963      * @cfg {String} header The header text to display in the Grid view.
4964      */
4965     /**
4966      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4967      * {@link Roo.data.Record} definition from which to draw the column's value. If not
4968      * specified, the column's index is used as an index into the Record's data Array.
4969      */
4970     /**
4971      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4972      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4973      */
4974     /**
4975      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4976      * Defaults to the value of the {@link #defaultSortable} property.
4977      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4978      */
4979     /**
4980      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
4981      */
4982     /**
4983      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
4984      */
4985     /**
4986      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4987      */
4988     /**
4989      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4990      */
4991     /**
4992      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4993      * given the cell's data value. See {@link #setRenderer}. If not specified, the
4994      * default renderer uses the raw data value. If an object is returned (bootstrap only)
4995      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4996      */
4997        /**
4998      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
4999      */
5000     /**
5001      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5002      */
5003     /**
5004      * @cfg {String} cursor (Optional)
5005      */
5006     /**
5007      * @cfg {String} tooltip (Optional)
5008      */
5009     /**
5010      * @cfg {Number} xs (Optional)
5011      */
5012     /**
5013      * @cfg {Number} sm (Optional)
5014      */
5015     /**
5016      * @cfg {Number} md (Optional)
5017      */
5018     /**
5019      * @cfg {Number} lg (Optional)
5020      */
5021     /**
5022      * Returns the id of the column at the specified index.
5023      * @param {Number} index The column index
5024      * @return {String} the id
5025      */
5026     getColumnId : function(index){
5027         return this.config[index].id;
5028     },
5029
5030     /**
5031      * Returns the column for a specified id.
5032      * @param {String} id The column id
5033      * @return {Object} the column
5034      */
5035     getColumnById : function(id){
5036         return this.lookup[id];
5037     },
5038
5039     
5040     /**
5041      * Returns the column for a specified dataIndex.
5042      * @param {String} dataIndex The column dataIndex
5043      * @return {Object|Boolean} the column or false if not found
5044      */
5045     getColumnByDataIndex: function(dataIndex){
5046         var index = this.findColumnIndex(dataIndex);
5047         return index > -1 ? this.config[index] : false;
5048     },
5049     
5050     /**
5051      * Returns the index for a specified column id.
5052      * @param {String} id The column id
5053      * @return {Number} the index, or -1 if not found
5054      */
5055     getIndexById : function(id){
5056         for(var i = 0, len = this.config.length; i < len; i++){
5057             if(this.config[i].id == id){
5058                 return i;
5059             }
5060         }
5061         return -1;
5062     },
5063     
5064     /**
5065      * Returns the index for a specified column dataIndex.
5066      * @param {String} dataIndex The column dataIndex
5067      * @return {Number} the index, or -1 if not found
5068      */
5069     
5070     findColumnIndex : function(dataIndex){
5071         for(var i = 0, len = this.config.length; i < len; i++){
5072             if(this.config[i].dataIndex == dataIndex){
5073                 return i;
5074             }
5075         }
5076         return -1;
5077     },
5078     
5079     
5080     moveColumn : function(oldIndex, newIndex){
5081         var c = this.config[oldIndex];
5082         this.config.splice(oldIndex, 1);
5083         this.config.splice(newIndex, 0, c);
5084         this.dataMap = null;
5085         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5086     },
5087
5088     isLocked : function(colIndex){
5089         return this.config[colIndex].locked === true;
5090     },
5091
5092     setLocked : function(colIndex, value, suppressEvent){
5093         if(this.isLocked(colIndex) == value){
5094             return;
5095         }
5096         this.config[colIndex].locked = value;
5097         if(!suppressEvent){
5098             this.fireEvent("columnlockchange", this, colIndex, value);
5099         }
5100     },
5101
5102     getTotalLockedWidth : function(){
5103         var totalWidth = 0;
5104         for(var i = 0; i < this.config.length; i++){
5105             if(this.isLocked(i) && !this.isHidden(i)){
5106                 this.totalWidth += this.getColumnWidth(i);
5107             }
5108         }
5109         return totalWidth;
5110     },
5111
5112     getLockedCount : function(){
5113         for(var i = 0, len = this.config.length; i < len; i++){
5114             if(!this.isLocked(i)){
5115                 return i;
5116             }
5117         }
5118         
5119         return this.config.length;
5120     },
5121
5122     /**
5123      * Returns the number of columns.
5124      * @return {Number}
5125      */
5126     getColumnCount : function(visibleOnly){
5127         if(visibleOnly === true){
5128             var c = 0;
5129             for(var i = 0, len = this.config.length; i < len; i++){
5130                 if(!this.isHidden(i)){
5131                     c++;
5132                 }
5133             }
5134             return c;
5135         }
5136         return this.config.length;
5137     },
5138
5139     /**
5140      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5141      * @param {Function} fn
5142      * @param {Object} scope (optional)
5143      * @return {Array} result
5144      */
5145     getColumnsBy : function(fn, scope){
5146         var r = [];
5147         for(var i = 0, len = this.config.length; i < len; i++){
5148             var c = this.config[i];
5149             if(fn.call(scope||this, c, i) === true){
5150                 r[r.length] = c;
5151             }
5152         }
5153         return r;
5154     },
5155
5156     /**
5157      * Returns true if the specified column is sortable.
5158      * @param {Number} col The column index
5159      * @return {Boolean}
5160      */
5161     isSortable : function(col){
5162         if(typeof this.config[col].sortable == "undefined"){
5163             return this.defaultSortable;
5164         }
5165         return this.config[col].sortable;
5166     },
5167
5168     /**
5169      * Returns the rendering (formatting) function defined for the column.
5170      * @param {Number} col The column index.
5171      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5172      */
5173     getRenderer : function(col){
5174         if(!this.config[col].renderer){
5175             return Roo.grid.ColumnModel.defaultRenderer;
5176         }
5177         return this.config[col].renderer;
5178     },
5179
5180     /**
5181      * Sets the rendering (formatting) function for a column.
5182      * @param {Number} col The column index
5183      * @param {Function} fn The function to use to process the cell's raw data
5184      * to return HTML markup for the grid view. The render function is called with
5185      * the following parameters:<ul>
5186      * <li>Data value.</li>
5187      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5188      * <li>css A CSS style string to apply to the table cell.</li>
5189      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5190      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5191      * <li>Row index</li>
5192      * <li>Column index</li>
5193      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5194      */
5195     setRenderer : function(col, fn){
5196         this.config[col].renderer = fn;
5197     },
5198
5199     /**
5200      * Returns the width for the specified column.
5201      * @param {Number} col The column index
5202      * @return {Number}
5203      */
5204     getColumnWidth : function(col){
5205         return this.config[col].width * 1 || this.defaultWidth;
5206     },
5207
5208     /**
5209      * Sets the width for a column.
5210      * @param {Number} col The column index
5211      * @param {Number} width The new width
5212      */
5213     setColumnWidth : function(col, width, suppressEvent){
5214         this.config[col].width = width;
5215         this.totalWidth = null;
5216         if(!suppressEvent){
5217              this.fireEvent("widthchange", this, col, width);
5218         }
5219     },
5220
5221     /**
5222      * Returns the total width of all columns.
5223      * @param {Boolean} includeHidden True to include hidden column widths
5224      * @return {Number}
5225      */
5226     getTotalWidth : function(includeHidden){
5227         if(!this.totalWidth){
5228             this.totalWidth = 0;
5229             for(var i = 0, len = this.config.length; i < len; i++){
5230                 if(includeHidden || !this.isHidden(i)){
5231                     this.totalWidth += this.getColumnWidth(i);
5232                 }
5233             }
5234         }
5235         return this.totalWidth;
5236     },
5237
5238     /**
5239      * Returns the header for the specified column.
5240      * @param {Number} col The column index
5241      * @return {String}
5242      */
5243     getColumnHeader : function(col){
5244         return this.config[col].header;
5245     },
5246
5247     /**
5248      * Sets the header for a column.
5249      * @param {Number} col The column index
5250      * @param {String} header The new header
5251      */
5252     setColumnHeader : function(col, header){
5253         this.config[col].header = header;
5254         this.fireEvent("headerchange", this, col, header);
5255     },
5256
5257     /**
5258      * Returns the tooltip for the specified column.
5259      * @param {Number} col The column index
5260      * @return {String}
5261      */
5262     getColumnTooltip : function(col){
5263             return this.config[col].tooltip;
5264     },
5265     /**
5266      * Sets the tooltip for a column.
5267      * @param {Number} col The column index
5268      * @param {String} tooltip The new tooltip
5269      */
5270     setColumnTooltip : function(col, tooltip){
5271             this.config[col].tooltip = tooltip;
5272     },
5273
5274     /**
5275      * Returns the dataIndex for the specified column.
5276      * @param {Number} col The column index
5277      * @return {Number}
5278      */
5279     getDataIndex : function(col){
5280         return this.config[col].dataIndex;
5281     },
5282
5283     /**
5284      * Sets the dataIndex for a column.
5285      * @param {Number} col The column index
5286      * @param {Number} dataIndex The new dataIndex
5287      */
5288     setDataIndex : function(col, dataIndex){
5289         this.config[col].dataIndex = dataIndex;
5290     },
5291
5292     
5293     
5294     /**
5295      * Returns true if the cell is editable.
5296      * @param {Number} colIndex The column index
5297      * @param {Number} rowIndex The row index - this is nto actually used..?
5298      * @return {Boolean}
5299      */
5300     isCellEditable : function(colIndex, rowIndex){
5301         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5302     },
5303
5304     /**
5305      * Returns the editor defined for the cell/column.
5306      * return false or null to disable editing.
5307      * @param {Number} colIndex The column index
5308      * @param {Number} rowIndex The row index
5309      * @return {Object}
5310      */
5311     getCellEditor : function(colIndex, rowIndex){
5312         return this.config[colIndex].editor;
5313     },
5314
5315     /**
5316      * Sets if a column is editable.
5317      * @param {Number} col The column index
5318      * @param {Boolean} editable True if the column is editable
5319      */
5320     setEditable : function(col, editable){
5321         this.config[col].editable = editable;
5322     },
5323
5324
5325     /**
5326      * Returns true if the column is hidden.
5327      * @param {Number} colIndex The column index
5328      * @return {Boolean}
5329      */
5330     isHidden : function(colIndex){
5331         return this.config[colIndex].hidden;
5332     },
5333
5334
5335     /**
5336      * Returns true if the column width cannot be changed
5337      */
5338     isFixed : function(colIndex){
5339         return this.config[colIndex].fixed;
5340     },
5341
5342     /**
5343      * Returns true if the column can be resized
5344      * @return {Boolean}
5345      */
5346     isResizable : function(colIndex){
5347         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5348     },
5349     /**
5350      * Sets if a column is hidden.
5351      * @param {Number} colIndex The column index
5352      * @param {Boolean} hidden True if the column is hidden
5353      */
5354     setHidden : function(colIndex, hidden){
5355         this.config[colIndex].hidden = hidden;
5356         this.totalWidth = null;
5357         this.fireEvent("hiddenchange", this, colIndex, hidden);
5358     },
5359
5360     /**
5361      * Sets the editor for a column.
5362      * @param {Number} col The column index
5363      * @param {Object} editor The editor object
5364      */
5365     setEditor : function(col, editor){
5366         this.config[col].editor = editor;
5367     }
5368 });
5369
5370 Roo.grid.ColumnModel.defaultRenderer = function(value){
5371         if(typeof value == "string" && value.length < 1){
5372             return "&#160;";
5373         }
5374         return value;
5375 };
5376
5377 // Alias for backwards compatibility
5378 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5379 /*
5380  * Based on:
5381  * Ext JS Library 1.1.1
5382  * Copyright(c) 2006-2007, Ext JS, LLC.
5383  *
5384  * Originally Released Under LGPL - original licence link has changed is not relivant.
5385  *
5386  * Fork - LGPL
5387  * <script type="text/javascript">
5388  */
5389  
5390 /**
5391  * @class Roo.LoadMask
5392  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5393  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5394  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5395  * element's UpdateManager load indicator and will be destroyed after the initial load.
5396  * @constructor
5397  * Create a new LoadMask
5398  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5399  * @param {Object} config The config object
5400  */
5401 Roo.LoadMask = function(el, config){
5402     this.el = Roo.get(el);
5403     Roo.apply(this, config);
5404     if(this.store){
5405         this.store.on('beforeload', this.onBeforeLoad, this);
5406         this.store.on('load', this.onLoad, this);
5407         this.store.on('loadexception', this.onLoadException, this);
5408         this.removeMask = false;
5409     }else{
5410         var um = this.el.getUpdateManager();
5411         um.showLoadIndicator = false; // disable the default indicator
5412         um.on('beforeupdate', this.onBeforeLoad, this);
5413         um.on('update', this.onLoad, this);
5414         um.on('failure', this.onLoad, this);
5415         this.removeMask = true;
5416     }
5417 };
5418
5419 Roo.LoadMask.prototype = {
5420     /**
5421      * @cfg {Boolean} removeMask
5422      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5423      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5424      */
5425     /**
5426      * @cfg {String} msg
5427      * The text to display in a centered loading message box (defaults to 'Loading...')
5428      */
5429     msg : 'Loading...',
5430     /**
5431      * @cfg {String} msgCls
5432      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5433      */
5434     msgCls : 'x-mask-loading',
5435
5436     /**
5437      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5438      * @type Boolean
5439      */
5440     disabled: false,
5441
5442     /**
5443      * Disables the mask to prevent it from being displayed
5444      */
5445     disable : function(){
5446        this.disabled = true;
5447     },
5448
5449     /**
5450      * Enables the mask so that it can be displayed
5451      */
5452     enable : function(){
5453         this.disabled = false;
5454     },
5455     
5456     onLoadException : function()
5457     {
5458         Roo.log(arguments);
5459         
5460         if (typeof(arguments[3]) != 'undefined') {
5461             Roo.MessageBox.alert("Error loading",arguments[3]);
5462         } 
5463         /*
5464         try {
5465             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5466                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5467             }   
5468         } catch(e) {
5469             
5470         }
5471         */
5472     
5473         
5474         
5475         this.el.unmask(this.removeMask);
5476     },
5477     // private
5478     onLoad : function()
5479     {
5480         this.el.unmask(this.removeMask);
5481     },
5482
5483     // private
5484     onBeforeLoad : function(){
5485         if(!this.disabled){
5486             this.el.mask(this.msg, this.msgCls);
5487         }
5488     },
5489
5490     // private
5491     destroy : function(){
5492         if(this.store){
5493             this.store.un('beforeload', this.onBeforeLoad, this);
5494             this.store.un('load', this.onLoad, this);
5495             this.store.un('loadexception', this.onLoadException, this);
5496         }else{
5497             var um = this.el.getUpdateManager();
5498             um.un('beforeupdate', this.onBeforeLoad, this);
5499             um.un('update', this.onLoad, this);
5500             um.un('failure', this.onLoad, this);
5501         }
5502     }
5503 };/*
5504  * - LGPL
5505  *
5506  * table
5507  * 
5508  */
5509
5510 /**
5511  * @class Roo.bootstrap.Table
5512  * @extends Roo.bootstrap.Component
5513  * Bootstrap Table class
5514  * @cfg {String} cls table class
5515  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5516  * @cfg {String} bgcolor Specifies the background color for a table
5517  * @cfg {Number} border Specifies whether the table cells should have borders or not
5518  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5519  * @cfg {Number} cellspacing Specifies the space between cells
5520  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5521  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5522  * @cfg {String} sortable Specifies that the table should be sortable
5523  * @cfg {String} summary Specifies a summary of the content of a table
5524  * @cfg {Number} width Specifies the width of a table
5525  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5526  * 
5527  * @cfg {boolean} striped Should the rows be alternative striped
5528  * @cfg {boolean} bordered Add borders to the table
5529  * @cfg {boolean} hover Add hover highlighting
5530  * @cfg {boolean} condensed Format condensed
5531  * @cfg {boolean} responsive Format condensed
5532  * @cfg {Boolean} loadMask (true|false) default false
5533  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5534  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5535  * @cfg {Boolean} rowSelection (true|false) default false
5536  * @cfg {Boolean} cellSelection (true|false) default false
5537  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5538  
5539  * 
5540  * @constructor
5541  * Create a new Table
5542  * @param {Object} config The config object
5543  */
5544
5545 Roo.bootstrap.Table = function(config){
5546     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5547     
5548     // BC...
5549     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5550     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5551     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5552     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5553     
5554     
5555     if (this.sm) {
5556         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5557         this.sm = this.selModel;
5558         this.sm.xmodule = this.xmodule || false;
5559     }
5560     if (this.cm && typeof(this.cm.config) == 'undefined') {
5561         this.colModel = new Roo.grid.ColumnModel(this.cm);
5562         this.cm = this.colModel;
5563         this.cm.xmodule = this.xmodule || false;
5564     }
5565     if (this.store) {
5566         this.store= Roo.factory(this.store, Roo.data);
5567         this.ds = this.store;
5568         this.ds.xmodule = this.xmodule || false;
5569          
5570     }
5571     if (this.footer && this.store) {
5572         this.footer.dataSource = this.ds;
5573         this.footer = Roo.factory(this.footer);
5574     }
5575     
5576     /** @private */
5577     this.addEvents({
5578         /**
5579          * @event cellclick
5580          * Fires when a cell is clicked
5581          * @param {Roo.bootstrap.Table} this
5582          * @param {Roo.Element} el
5583          * @param {Number} rowIndex
5584          * @param {Number} columnIndex
5585          * @param {Roo.EventObject} e
5586          */
5587         "cellclick" : true,
5588         /**
5589          * @event celldblclick
5590          * Fires when a cell is double clicked
5591          * @param {Roo.bootstrap.Table} this
5592          * @param {Roo.Element} el
5593          * @param {Number} rowIndex
5594          * @param {Number} columnIndex
5595          * @param {Roo.EventObject} e
5596          */
5597         "celldblclick" : true,
5598         /**
5599          * @event rowclick
5600          * Fires when a row is clicked
5601          * @param {Roo.bootstrap.Table} this
5602          * @param {Roo.Element} el
5603          * @param {Number} rowIndex
5604          * @param {Roo.EventObject} e
5605          */
5606         "rowclick" : true,
5607         /**
5608          * @event rowdblclick
5609          * Fires when a row is double clicked
5610          * @param {Roo.bootstrap.Table} this
5611          * @param {Roo.Element} el
5612          * @param {Number} rowIndex
5613          * @param {Roo.EventObject} e
5614          */
5615         "rowdblclick" : true,
5616         /**
5617          * @event mouseover
5618          * Fires when a mouseover occur
5619          * @param {Roo.bootstrap.Table} this
5620          * @param {Roo.Element} el
5621          * @param {Number} rowIndex
5622          * @param {Number} columnIndex
5623          * @param {Roo.EventObject} e
5624          */
5625         "mouseover" : true,
5626         /**
5627          * @event mouseout
5628          * Fires when a mouseout occur
5629          * @param {Roo.bootstrap.Table} this
5630          * @param {Roo.Element} el
5631          * @param {Number} rowIndex
5632          * @param {Number} columnIndex
5633          * @param {Roo.EventObject} e
5634          */
5635         "mouseout" : true,
5636         /**
5637          * @event rowclass
5638          * Fires when a row is rendered, so you can change add a style to it.
5639          * @param {Roo.bootstrap.Table} this
5640          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5641          */
5642         'rowclass' : true,
5643           /**
5644          * @event rowsrendered
5645          * Fires when all the  rows have been rendered
5646          * @param {Roo.bootstrap.Table} this
5647          */
5648         'rowsrendered' : true
5649         
5650     });
5651 };
5652
5653 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5654     
5655     cls: false,
5656     align: false,
5657     bgcolor: false,
5658     border: false,
5659     cellpadding: false,
5660     cellspacing: false,
5661     frame: false,
5662     rules: false,
5663     sortable: false,
5664     summary: false,
5665     width: false,
5666     striped : false,
5667     bordered: false,
5668     hover:  false,
5669     condensed : false,
5670     responsive : false,
5671     sm : false,
5672     cm : false,
5673     store : false,
5674     loadMask : false,
5675     footerShow : true,
5676     headerShow : true,
5677   
5678     rowSelection : false,
5679     cellSelection : false,
5680     layout : false,
5681     
5682     // Roo.Element - the tbody
5683     mainBody: false, 
5684     
5685     getAutoCreate : function(){
5686         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5687         
5688         cfg = {
5689             tag: 'table',
5690             cls : 'table',
5691             cn : []
5692         };
5693             
5694         if (this.striped) {
5695             cfg.cls += ' table-striped';
5696         }
5697         
5698         if (this.hover) {
5699             cfg.cls += ' table-hover';
5700         }
5701         if (this.bordered) {
5702             cfg.cls += ' table-bordered';
5703         }
5704         if (this.condensed) {
5705             cfg.cls += ' table-condensed';
5706         }
5707         if (this.responsive) {
5708             cfg.cls += ' table-responsive';
5709         }
5710         
5711         if (this.cls) {
5712             cfg.cls+=  ' ' +this.cls;
5713         }
5714         
5715         // this lot should be simplifed...
5716         
5717         if (this.align) {
5718             cfg.align=this.align;
5719         }
5720         if (this.bgcolor) {
5721             cfg.bgcolor=this.bgcolor;
5722         }
5723         if (this.border) {
5724             cfg.border=this.border;
5725         }
5726         if (this.cellpadding) {
5727             cfg.cellpadding=this.cellpadding;
5728         }
5729         if (this.cellspacing) {
5730             cfg.cellspacing=this.cellspacing;
5731         }
5732         if (this.frame) {
5733             cfg.frame=this.frame;
5734         }
5735         if (this.rules) {
5736             cfg.rules=this.rules;
5737         }
5738         if (this.sortable) {
5739             cfg.sortable=this.sortable;
5740         }
5741         if (this.summary) {
5742             cfg.summary=this.summary;
5743         }
5744         if (this.width) {
5745             cfg.width=this.width;
5746         }
5747         if (this.layout) {
5748             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5749         }
5750         
5751         if(this.store || this.cm){
5752             if(this.headerShow){
5753                 cfg.cn.push(this.renderHeader());
5754             }
5755             
5756             cfg.cn.push(this.renderBody());
5757             
5758             if(this.footerShow){
5759                 cfg.cn.push(this.renderFooter());
5760             }
5761             
5762             cfg.cls+=  ' TableGrid';
5763         }
5764         
5765         return { cn : [ cfg ] };
5766     },
5767     
5768     initEvents : function()
5769     {   
5770         if(!this.store || !this.cm){
5771             return;
5772         }
5773         
5774         //Roo.log('initEvents with ds!!!!');
5775         
5776         this.mainBody = this.el.select('tbody', true).first();
5777         
5778         
5779         var _this = this;
5780         
5781         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5782             e.on('click', _this.sort, _this);
5783         });
5784         
5785         this.el.on("click", this.onClick, this);
5786         this.el.on("dblclick", this.onDblClick, this);
5787         
5788         // why is this done????? = it breaks dialogs??
5789         //this.parent().el.setStyle('position', 'relative');
5790         
5791         
5792         if (this.footer) {
5793             this.footer.parentId = this.id;
5794             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5795         }
5796         
5797         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5798         
5799         this.store.on('load', this.onLoad, this);
5800         this.store.on('beforeload', this.onBeforeLoad, this);
5801         this.store.on('update', this.onUpdate, this);
5802         this.store.on('add', this.onAdd, this);
5803         
5804     },
5805     
5806     onMouseover : function(e, el)
5807     {
5808         var cell = Roo.get(el);
5809         
5810         if(!cell){
5811             return;
5812         }
5813         
5814         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5815             cell = cell.findParent('td', false, true);
5816         }
5817         
5818         var row = cell.findParent('tr', false, true);
5819         var cellIndex = cell.dom.cellIndex;
5820         var rowIndex = row.dom.rowIndex - 1; // start from 0
5821         
5822         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5823         
5824     },
5825     
5826     onMouseout : function(e, el)
5827     {
5828         var cell = Roo.get(el);
5829         
5830         if(!cell){
5831             return;
5832         }
5833         
5834         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5835             cell = cell.findParent('td', false, true);
5836         }
5837         
5838         var row = cell.findParent('tr', false, true);
5839         var cellIndex = cell.dom.cellIndex;
5840         var rowIndex = row.dom.rowIndex - 1; // start from 0
5841         
5842         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5843         
5844     },
5845     
5846     onClick : function(e, el)
5847     {
5848         var cell = Roo.get(el);
5849         
5850         if(!cell || (!this.cellSelection && !this.rowSelection)){
5851             return;
5852         }
5853         
5854         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5855             cell = cell.findParent('td', false, true);
5856         }
5857         
5858         if(!cell || typeof(cell) == 'undefined'){
5859             return;
5860         }
5861         
5862         var row = cell.findParent('tr', false, true);
5863         
5864         if(!row || typeof(row) == 'undefined'){
5865             return;
5866         }
5867         
5868         var cellIndex = cell.dom.cellIndex;
5869         var rowIndex = this.getRowIndex(row);
5870         
5871         // why??? - should these not be based on SelectionModel?
5872         if(this.cellSelection){
5873             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5874         }
5875         
5876         if(this.rowSelection){
5877             this.fireEvent('rowclick', this, row, rowIndex, e);
5878         }
5879         
5880         
5881     },
5882     
5883     onDblClick : function(e,el)
5884     {
5885         var cell = Roo.get(el);
5886         
5887         if(!cell || (!this.CellSelection && !this.RowSelection)){
5888             return;
5889         }
5890         
5891         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5892             cell = cell.findParent('td', false, true);
5893         }
5894         
5895         if(!cell || typeof(cell) == 'undefined'){
5896             return;
5897         }
5898         
5899         var row = cell.findParent('tr', false, true);
5900         
5901         if(!row || typeof(row) == 'undefined'){
5902             return;
5903         }
5904         
5905         var cellIndex = cell.dom.cellIndex;
5906         var rowIndex = this.getRowIndex(row);
5907         
5908         if(this.CellSelection){
5909             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5910         }
5911         
5912         if(this.RowSelection){
5913             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5914         }
5915     },
5916     
5917     sort : function(e,el)
5918     {
5919         var col = Roo.get(el);
5920         
5921         if(!col.hasClass('sortable')){
5922             return;
5923         }
5924         
5925         var sort = col.attr('sort');
5926         var dir = 'ASC';
5927         
5928         if(col.hasClass('glyphicon-arrow-up')){
5929             dir = 'DESC';
5930         }
5931         
5932         this.store.sortInfo = {field : sort, direction : dir};
5933         
5934         if (this.footer) {
5935             Roo.log("calling footer first");
5936             this.footer.onClick('first');
5937         } else {
5938         
5939             this.store.load({ params : { start : 0 } });
5940         }
5941     },
5942     
5943     renderHeader : function()
5944     {
5945         var header = {
5946             tag: 'thead',
5947             cn : []
5948         };
5949         
5950         var cm = this.cm;
5951         
5952         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5953             
5954             var config = cm.config[i];
5955             
5956             var c = {
5957                 tag: 'th',
5958                 style : '',
5959                 html: cm.getColumnHeader(i)
5960             };
5961             
5962             var hh = '';
5963             
5964             if(typeof(config.lgHeader) != 'undefined'){
5965                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
5966             }
5967             
5968             if(typeof(config.mdHeader) != 'undefined'){
5969                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
5970             }
5971             
5972             if(typeof(config.smHeader) != 'undefined'){
5973                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
5974             }
5975             
5976             if(typeof(config.xsHeader) != 'undefined'){
5977                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
5978             }
5979             
5980             if(hh.length){
5981                 c.html = hh;
5982             }
5983             
5984             if(typeof(config.tooltip) != 'undefined'){
5985                 c.tooltip = config.tooltip;
5986             }
5987             
5988             if(typeof(config.colspan) != 'undefined'){
5989                 c.colspan = config.colspan;
5990             }
5991             
5992             if(typeof(config.hidden) != 'undefined' && config.hidden){
5993                 c.style += ' display:none;';
5994             }
5995             
5996             if(typeof(config.dataIndex) != 'undefined'){
5997                 c.sort = config.dataIndex;
5998             }
5999             
6000             if(typeof(config.sortable) != 'undefined' && config.sortable){
6001                 c.cls = 'sortable';
6002             }
6003             
6004             if(typeof(config.align) != 'undefined' && config.align.length){
6005                 c.style += ' text-align:' + config.align + ';';
6006             }
6007             
6008             if(typeof(config.width) != 'undefined'){
6009                 c.style += ' width:' + config.width + 'px;';
6010             }
6011             
6012             if(typeof(config.cls) != 'undefined'){
6013                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6014             }
6015             
6016             ['xs','sm','md','lg'].map(function(size){
6017                 
6018                 if(typeof(config[size]) == 'undefined'){
6019                     return;
6020                 }
6021                 
6022                 if (!config[size]) { // 0 = hidden
6023                     c.cls += ' hidden-' + size;
6024                     return;
6025                 }
6026                 
6027                 c.cls += ' col-' + size + '-' + config[size];
6028
6029             });
6030             
6031             header.cn.push(c)
6032         }
6033         
6034         return header;
6035     },
6036     
6037     renderBody : function()
6038     {
6039         var body = {
6040             tag: 'tbody',
6041             cn : [
6042                 {
6043                     tag: 'tr',
6044                     cn : [
6045                         {
6046                             tag : 'td',
6047                             colspan :  this.cm.getColumnCount()
6048                         }
6049                     ]
6050                 }
6051             ]
6052         };
6053         
6054         return body;
6055     },
6056     
6057     renderFooter : function()
6058     {
6059         var footer = {
6060             tag: 'tfoot',
6061             cn : [
6062                 {
6063                     tag: 'tr',
6064                     cn : [
6065                         {
6066                             tag : 'td',
6067                             colspan :  this.cm.getColumnCount()
6068                         }
6069                     ]
6070                 }
6071             ]
6072         };
6073         
6074         return footer;
6075     },
6076     
6077     
6078     
6079     onLoad : function()
6080     {
6081 //        Roo.log('ds onload');
6082         this.clear();
6083         
6084         var _this = this;
6085         var cm = this.cm;
6086         var ds = this.store;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6090             
6091             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6092                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6093             }
6094             
6095             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6096                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6097             }
6098         });
6099         
6100         var tbody =  this.mainBody;
6101               
6102         if(ds.getCount() > 0){
6103             ds.data.each(function(d,rowIndex){
6104                 var row =  this.renderRow(cm, ds, rowIndex);
6105                 
6106                 tbody.createChild(row);
6107                 
6108                 var _this = this;
6109                 
6110                 if(row.cellObjects.length){
6111                     Roo.each(row.cellObjects, function(r){
6112                         _this.renderCellObject(r);
6113                     })
6114                 }
6115                 
6116             }, this);
6117         }
6118         
6119         Roo.each(this.el.select('tbody td', true).elements, function(e){
6120             e.on('mouseover', _this.onMouseover, _this);
6121         });
6122         
6123         Roo.each(this.el.select('tbody td', true).elements, function(e){
6124             e.on('mouseout', _this.onMouseout, _this);
6125         });
6126         this.fireEvent('rowsrendered', this);
6127         //if(this.loadMask){
6128         //    this.maskEl.hide();
6129         //}
6130     },
6131     
6132     
6133     onUpdate : function(ds,record)
6134     {
6135         this.refreshRow(record);
6136     },
6137     
6138     onRemove : function(ds, record, index, isUpdate){
6139         if(isUpdate !== true){
6140             this.fireEvent("beforerowremoved", this, index, record);
6141         }
6142         var bt = this.mainBody.dom;
6143         
6144         var rows = this.el.select('tbody > tr', true).elements;
6145         
6146         if(typeof(rows[index]) != 'undefined'){
6147             bt.removeChild(rows[index].dom);
6148         }
6149         
6150 //        if(bt.rows[index]){
6151 //            bt.removeChild(bt.rows[index]);
6152 //        }
6153         
6154         if(isUpdate !== true){
6155             //this.stripeRows(index);
6156             //this.syncRowHeights(index, index);
6157             //this.layout();
6158             this.fireEvent("rowremoved", this, index, record);
6159         }
6160     },
6161     
6162     onAdd : function(ds, records, rowIndex)
6163     {
6164         //Roo.log('on Add called');
6165         // - note this does not handle multiple adding very well..
6166         var bt = this.mainBody.dom;
6167         for (var i =0 ; i < records.length;i++) {
6168             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6169             //Roo.log(records[i]);
6170             //Roo.log(this.store.getAt(rowIndex+i));
6171             this.insertRow(this.store, rowIndex + i, false);
6172             return;
6173         }
6174         
6175     },
6176     
6177     
6178     refreshRow : function(record){
6179         var ds = this.store, index;
6180         if(typeof record == 'number'){
6181             index = record;
6182             record = ds.getAt(index);
6183         }else{
6184             index = ds.indexOf(record);
6185         }
6186         this.insertRow(ds, index, true);
6187         this.onRemove(ds, record, index+1, true);
6188         //this.syncRowHeights(index, index);
6189         //this.layout();
6190         this.fireEvent("rowupdated", this, index, record);
6191     },
6192     
6193     insertRow : function(dm, rowIndex, isUpdate){
6194         
6195         if(!isUpdate){
6196             this.fireEvent("beforerowsinserted", this, rowIndex);
6197         }
6198             //var s = this.getScrollState();
6199         var row = this.renderRow(this.cm, this.store, rowIndex);
6200         // insert before rowIndex..
6201         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6202         
6203         var _this = this;
6204                 
6205         if(row.cellObjects.length){
6206             Roo.each(row.cellObjects, function(r){
6207                 _this.renderCellObject(r);
6208             })
6209         }
6210             
6211         if(!isUpdate){
6212             this.fireEvent("rowsinserted", this, rowIndex);
6213             //this.syncRowHeights(firstRow, lastRow);
6214             //this.stripeRows(firstRow);
6215             //this.layout();
6216         }
6217         
6218     },
6219     
6220     
6221     getRowDom : function(rowIndex)
6222     {
6223         var rows = this.el.select('tbody > tr', true).elements;
6224         
6225         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6226         
6227     },
6228     // returns the object tree for a tr..
6229   
6230     
6231     renderRow : function(cm, ds, rowIndex) 
6232     {
6233         
6234         var d = ds.getAt(rowIndex);
6235         
6236         var row = {
6237             tag : 'tr',
6238             cn : []
6239         };
6240             
6241         var cellObjects = [];
6242         
6243         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6244             var config = cm.config[i];
6245             
6246             var renderer = cm.getRenderer(i);
6247             var value = '';
6248             var id = false;
6249             
6250             if(typeof(renderer) !== 'undefined'){
6251                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6252             }
6253             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6254             // and are rendered into the cells after the row is rendered - using the id for the element.
6255             
6256             if(typeof(value) === 'object'){
6257                 id = Roo.id();
6258                 cellObjects.push({
6259                     container : id,
6260                     cfg : value 
6261                 })
6262             }
6263             
6264             var rowcfg = {
6265                 record: d,
6266                 rowIndex : rowIndex,
6267                 colIndex : i,
6268                 rowClass : ''
6269             };
6270
6271             this.fireEvent('rowclass', this, rowcfg);
6272             
6273             var td = {
6274                 tag: 'td',
6275                 cls : rowcfg.rowClass,
6276                 style: '',
6277                 html: (typeof(value) === 'object') ? '' : value
6278             };
6279             
6280             if (id) {
6281                 td.id = id;
6282             }
6283             
6284             if(typeof(config.colspan) != 'undefined'){
6285                 td.colspan = config.colspan;
6286             }
6287             
6288             if(typeof(config.hidden) != 'undefined' && config.hidden){
6289                 td.style += ' display:none;';
6290             }
6291             
6292             if(typeof(config.align) != 'undefined' && config.align.length){
6293                 td.style += ' text-align:' + config.align + ';';
6294             }
6295             
6296             if(typeof(config.width) != 'undefined'){
6297                 td.style += ' width:' +  config.width + 'px;';
6298             }
6299             
6300             if(typeof(config.cursor) != 'undefined'){
6301                 td.style += ' cursor:' +  config.cursor + ';';
6302             }
6303             
6304             if(typeof(config.cls) != 'undefined'){
6305                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6306             }
6307             
6308             ['xs','sm','md','lg'].map(function(size){
6309                 
6310                 if(typeof(config[size]) == 'undefined'){
6311                     return;
6312                 }
6313                 
6314                 if (!config[size]) { // 0 = hidden
6315                     td.cls += ' hidden-' + size;
6316                     return;
6317                 }
6318                 
6319                 td.cls += ' col-' + size + '-' + config[size];
6320
6321             });
6322              
6323             row.cn.push(td);
6324            
6325         }
6326         
6327         row.cellObjects = cellObjects;
6328         
6329         return row;
6330           
6331     },
6332     
6333     
6334     
6335     onBeforeLoad : function()
6336     {
6337         //Roo.log('ds onBeforeLoad');
6338         
6339         //this.clear();
6340         
6341         //if(this.loadMask){
6342         //    this.maskEl.show();
6343         //}
6344     },
6345      /**
6346      * Remove all rows
6347      */
6348     clear : function()
6349     {
6350         this.el.select('tbody', true).first().dom.innerHTML = '';
6351     },
6352     /**
6353      * Show or hide a row.
6354      * @param {Number} rowIndex to show or hide
6355      * @param {Boolean} state hide
6356      */
6357     setRowVisibility : function(rowIndex, state)
6358     {
6359         var bt = this.mainBody.dom;
6360         
6361         var rows = this.el.select('tbody > tr', true).elements;
6362         
6363         if(typeof(rows[rowIndex]) == 'undefined'){
6364             return;
6365         }
6366         rows[rowIndex].dom.style.display = state ? '' : 'none';
6367     },
6368     
6369     
6370     getSelectionModel : function(){
6371         if(!this.selModel){
6372             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6373         }
6374         return this.selModel;
6375     },
6376     /*
6377      * Render the Roo.bootstrap object from renderder
6378      */
6379     renderCellObject : function(r)
6380     {
6381         var _this = this;
6382         
6383         var t = r.cfg.render(r.container);
6384         
6385         if(r.cfg.cn){
6386             Roo.each(r.cfg.cn, function(c){
6387                 var child = {
6388                     container: t.getChildContainer(),
6389                     cfg: c
6390                 };
6391                 _this.renderCellObject(child);
6392             })
6393         }
6394     },
6395     
6396     getRowIndex : function(row)
6397     {
6398         var rowIndex = -1;
6399         
6400         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6401             if(el != row){
6402                 return;
6403             }
6404             
6405             rowIndex = index;
6406         });
6407         
6408         return rowIndex;
6409     }
6410    
6411 });
6412
6413  
6414
6415  /*
6416  * - LGPL
6417  *
6418  * table cell
6419  * 
6420  */
6421
6422 /**
6423  * @class Roo.bootstrap.TableCell
6424  * @extends Roo.bootstrap.Component
6425  * Bootstrap TableCell class
6426  * @cfg {String} html cell contain text
6427  * @cfg {String} cls cell class
6428  * @cfg {String} tag cell tag (td|th) default td
6429  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6430  * @cfg {String} align Aligns the content in a cell
6431  * @cfg {String} axis Categorizes cells
6432  * @cfg {String} bgcolor Specifies the background color of a cell
6433  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6434  * @cfg {Number} colspan Specifies the number of columns a cell should span
6435  * @cfg {String} headers Specifies one or more header cells a cell is related to
6436  * @cfg {Number} height Sets the height of a cell
6437  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6438  * @cfg {Number} rowspan Sets the number of rows a cell should span
6439  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6440  * @cfg {String} valign Vertical aligns the content in a cell
6441  * @cfg {Number} width Specifies the width of a cell
6442  * 
6443  * @constructor
6444  * Create a new TableCell
6445  * @param {Object} config The config object
6446  */
6447
6448 Roo.bootstrap.TableCell = function(config){
6449     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6450 };
6451
6452 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6453     
6454     html: false,
6455     cls: false,
6456     tag: false,
6457     abbr: false,
6458     align: false,
6459     axis: false,
6460     bgcolor: false,
6461     charoff: false,
6462     colspan: false,
6463     headers: false,
6464     height: false,
6465     nowrap: false,
6466     rowspan: false,
6467     scope: false,
6468     valign: false,
6469     width: false,
6470     
6471     
6472     getAutoCreate : function(){
6473         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6474         
6475         cfg = {
6476             tag: 'td'
6477         };
6478         
6479         if(this.tag){
6480             cfg.tag = this.tag;
6481         }
6482         
6483         if (this.html) {
6484             cfg.html=this.html
6485         }
6486         if (this.cls) {
6487             cfg.cls=this.cls
6488         }
6489         if (this.abbr) {
6490             cfg.abbr=this.abbr
6491         }
6492         if (this.align) {
6493             cfg.align=this.align
6494         }
6495         if (this.axis) {
6496             cfg.axis=this.axis
6497         }
6498         if (this.bgcolor) {
6499             cfg.bgcolor=this.bgcolor
6500         }
6501         if (this.charoff) {
6502             cfg.charoff=this.charoff
6503         }
6504         if (this.colspan) {
6505             cfg.colspan=this.colspan
6506         }
6507         if (this.headers) {
6508             cfg.headers=this.headers
6509         }
6510         if (this.height) {
6511             cfg.height=this.height
6512         }
6513         if (this.nowrap) {
6514             cfg.nowrap=this.nowrap
6515         }
6516         if (this.rowspan) {
6517             cfg.rowspan=this.rowspan
6518         }
6519         if (this.scope) {
6520             cfg.scope=this.scope
6521         }
6522         if (this.valign) {
6523             cfg.valign=this.valign
6524         }
6525         if (this.width) {
6526             cfg.width=this.width
6527         }
6528         
6529         
6530         return cfg;
6531     }
6532    
6533 });
6534
6535  
6536
6537  /*
6538  * - LGPL
6539  *
6540  * table row
6541  * 
6542  */
6543
6544 /**
6545  * @class Roo.bootstrap.TableRow
6546  * @extends Roo.bootstrap.Component
6547  * Bootstrap TableRow class
6548  * @cfg {String} cls row class
6549  * @cfg {String} align Aligns the content in a table row
6550  * @cfg {String} bgcolor Specifies a background color for a table row
6551  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6552  * @cfg {String} valign Vertical aligns the content in a table row
6553  * 
6554  * @constructor
6555  * Create a new TableRow
6556  * @param {Object} config The config object
6557  */
6558
6559 Roo.bootstrap.TableRow = function(config){
6560     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6561 };
6562
6563 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6564     
6565     cls: false,
6566     align: false,
6567     bgcolor: false,
6568     charoff: false,
6569     valign: false,
6570     
6571     getAutoCreate : function(){
6572         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6573         
6574         cfg = {
6575             tag: 'tr'
6576         };
6577             
6578         if(this.cls){
6579             cfg.cls = this.cls;
6580         }
6581         if(this.align){
6582             cfg.align = this.align;
6583         }
6584         if(this.bgcolor){
6585             cfg.bgcolor = this.bgcolor;
6586         }
6587         if(this.charoff){
6588             cfg.charoff = this.charoff;
6589         }
6590         if(this.valign){
6591             cfg.valign = this.valign;
6592         }
6593         
6594         return cfg;
6595     }
6596    
6597 });
6598
6599  
6600
6601  /*
6602  * - LGPL
6603  *
6604  * table body
6605  * 
6606  */
6607
6608 /**
6609  * @class Roo.bootstrap.TableBody
6610  * @extends Roo.bootstrap.Component
6611  * Bootstrap TableBody class
6612  * @cfg {String} cls element class
6613  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6614  * @cfg {String} align Aligns the content inside the element
6615  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6616  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6617  * 
6618  * @constructor
6619  * Create a new TableBody
6620  * @param {Object} config The config object
6621  */
6622
6623 Roo.bootstrap.TableBody = function(config){
6624     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6625 };
6626
6627 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6628     
6629     cls: false,
6630     tag: false,
6631     align: false,
6632     charoff: false,
6633     valign: false,
6634     
6635     getAutoCreate : function(){
6636         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6637         
6638         cfg = {
6639             tag: 'tbody'
6640         };
6641             
6642         if (this.cls) {
6643             cfg.cls=this.cls
6644         }
6645         if(this.tag){
6646             cfg.tag = this.tag;
6647         }
6648         
6649         if(this.align){
6650             cfg.align = this.align;
6651         }
6652         if(this.charoff){
6653             cfg.charoff = this.charoff;
6654         }
6655         if(this.valign){
6656             cfg.valign = this.valign;
6657         }
6658         
6659         return cfg;
6660     }
6661     
6662     
6663 //    initEvents : function()
6664 //    {
6665 //        
6666 //        if(!this.store){
6667 //            return;
6668 //        }
6669 //        
6670 //        this.store = Roo.factory(this.store, Roo.data);
6671 //        this.store.on('load', this.onLoad, this);
6672 //        
6673 //        this.store.load();
6674 //        
6675 //    },
6676 //    
6677 //    onLoad: function () 
6678 //    {   
6679 //        this.fireEvent('load', this);
6680 //    }
6681 //    
6682 //   
6683 });
6684
6685  
6686
6687  /*
6688  * Based on:
6689  * Ext JS Library 1.1.1
6690  * Copyright(c) 2006-2007, Ext JS, LLC.
6691  *
6692  * Originally Released Under LGPL - original licence link has changed is not relivant.
6693  *
6694  * Fork - LGPL
6695  * <script type="text/javascript">
6696  */
6697
6698 // as we use this in bootstrap.
6699 Roo.namespace('Roo.form');
6700  /**
6701  * @class Roo.form.Action
6702  * Internal Class used to handle form actions
6703  * @constructor
6704  * @param {Roo.form.BasicForm} el The form element or its id
6705  * @param {Object} config Configuration options
6706  */
6707
6708  
6709  
6710 // define the action interface
6711 Roo.form.Action = function(form, options){
6712     this.form = form;
6713     this.options = options || {};
6714 };
6715 /**
6716  * Client Validation Failed
6717  * @const 
6718  */
6719 Roo.form.Action.CLIENT_INVALID = 'client';
6720 /**
6721  * Server Validation Failed
6722  * @const 
6723  */
6724 Roo.form.Action.SERVER_INVALID = 'server';
6725  /**
6726  * Connect to Server Failed
6727  * @const 
6728  */
6729 Roo.form.Action.CONNECT_FAILURE = 'connect';
6730 /**
6731  * Reading Data from Server Failed
6732  * @const 
6733  */
6734 Roo.form.Action.LOAD_FAILURE = 'load';
6735
6736 Roo.form.Action.prototype = {
6737     type : 'default',
6738     failureType : undefined,
6739     response : undefined,
6740     result : undefined,
6741
6742     // interface method
6743     run : function(options){
6744
6745     },
6746
6747     // interface method
6748     success : function(response){
6749
6750     },
6751
6752     // interface method
6753     handleResponse : function(response){
6754
6755     },
6756
6757     // default connection failure
6758     failure : function(response){
6759         
6760         this.response = response;
6761         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6762         this.form.afterAction(this, false);
6763     },
6764
6765     processResponse : function(response){
6766         this.response = response;
6767         if(!response.responseText){
6768             return true;
6769         }
6770         this.result = this.handleResponse(response);
6771         return this.result;
6772     },
6773
6774     // utility functions used internally
6775     getUrl : function(appendParams){
6776         var url = this.options.url || this.form.url || this.form.el.dom.action;
6777         if(appendParams){
6778             var p = this.getParams();
6779             if(p){
6780                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6781             }
6782         }
6783         return url;
6784     },
6785
6786     getMethod : function(){
6787         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6788     },
6789
6790     getParams : function(){
6791         var bp = this.form.baseParams;
6792         var p = this.options.params;
6793         if(p){
6794             if(typeof p == "object"){
6795                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6796             }else if(typeof p == 'string' && bp){
6797                 p += '&' + Roo.urlEncode(bp);
6798             }
6799         }else if(bp){
6800             p = Roo.urlEncode(bp);
6801         }
6802         return p;
6803     },
6804
6805     createCallback : function(){
6806         return {
6807             success: this.success,
6808             failure: this.failure,
6809             scope: this,
6810             timeout: (this.form.timeout*1000),
6811             upload: this.form.fileUpload ? this.success : undefined
6812         };
6813     }
6814 };
6815
6816 Roo.form.Action.Submit = function(form, options){
6817     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6818 };
6819
6820 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6821     type : 'submit',
6822
6823     haveProgress : false,
6824     uploadComplete : false,
6825     
6826     // uploadProgress indicator.
6827     uploadProgress : function()
6828     {
6829         if (!this.form.progressUrl) {
6830             return;
6831         }
6832         
6833         if (!this.haveProgress) {
6834             Roo.MessageBox.progress("Uploading", "Uploading");
6835         }
6836         if (this.uploadComplete) {
6837            Roo.MessageBox.hide();
6838            return;
6839         }
6840         
6841         this.haveProgress = true;
6842    
6843         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6844         
6845         var c = new Roo.data.Connection();
6846         c.request({
6847             url : this.form.progressUrl,
6848             params: {
6849                 id : uid
6850             },
6851             method: 'GET',
6852             success : function(req){
6853                //console.log(data);
6854                 var rdata = false;
6855                 var edata;
6856                 try  {
6857                    rdata = Roo.decode(req.responseText)
6858                 } catch (e) {
6859                     Roo.log("Invalid data from server..");
6860                     Roo.log(edata);
6861                     return;
6862                 }
6863                 if (!rdata || !rdata.success) {
6864                     Roo.log(rdata);
6865                     Roo.MessageBox.alert(Roo.encode(rdata));
6866                     return;
6867                 }
6868                 var data = rdata.data;
6869                 
6870                 if (this.uploadComplete) {
6871                    Roo.MessageBox.hide();
6872                    return;
6873                 }
6874                    
6875                 if (data){
6876                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6877                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6878                     );
6879                 }
6880                 this.uploadProgress.defer(2000,this);
6881             },
6882        
6883             failure: function(data) {
6884                 Roo.log('progress url failed ');
6885                 Roo.log(data);
6886             },
6887             scope : this
6888         });
6889            
6890     },
6891     
6892     
6893     run : function()
6894     {
6895         // run get Values on the form, so it syncs any secondary forms.
6896         this.form.getValues();
6897         
6898         var o = this.options;
6899         var method = this.getMethod();
6900         var isPost = method == 'POST';
6901         if(o.clientValidation === false || this.form.isValid()){
6902             
6903             if (this.form.progressUrl) {
6904                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6905                     (new Date() * 1) + '' + Math.random());
6906                     
6907             } 
6908             
6909             
6910             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6911                 form:this.form.el.dom,
6912                 url:this.getUrl(!isPost),
6913                 method: method,
6914                 params:isPost ? this.getParams() : null,
6915                 isUpload: this.form.fileUpload
6916             }));
6917             
6918             this.uploadProgress();
6919
6920         }else if (o.clientValidation !== false){ // client validation failed
6921             this.failureType = Roo.form.Action.CLIENT_INVALID;
6922             this.form.afterAction(this, false);
6923         }
6924     },
6925
6926     success : function(response)
6927     {
6928         this.uploadComplete= true;
6929         if (this.haveProgress) {
6930             Roo.MessageBox.hide();
6931         }
6932         
6933         
6934         var result = this.processResponse(response);
6935         if(result === true || result.success){
6936             this.form.afterAction(this, true);
6937             return;
6938         }
6939         if(result.errors){
6940             this.form.markInvalid(result.errors);
6941             this.failureType = Roo.form.Action.SERVER_INVALID;
6942         }
6943         this.form.afterAction(this, false);
6944     },
6945     failure : function(response)
6946     {
6947         this.uploadComplete= true;
6948         if (this.haveProgress) {
6949             Roo.MessageBox.hide();
6950         }
6951         
6952         this.response = response;
6953         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6954         this.form.afterAction(this, false);
6955     },
6956     
6957     handleResponse : function(response){
6958         if(this.form.errorReader){
6959             var rs = this.form.errorReader.read(response);
6960             var errors = [];
6961             if(rs.records){
6962                 for(var i = 0, len = rs.records.length; i < len; i++) {
6963                     var r = rs.records[i];
6964                     errors[i] = r.data;
6965                 }
6966             }
6967             if(errors.length < 1){
6968                 errors = null;
6969             }
6970             return {
6971                 success : rs.success,
6972                 errors : errors
6973             };
6974         }
6975         var ret = false;
6976         try {
6977             ret = Roo.decode(response.responseText);
6978         } catch (e) {
6979             ret = {
6980                 success: false,
6981                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6982                 errors : []
6983             };
6984         }
6985         return ret;
6986         
6987     }
6988 });
6989
6990
6991 Roo.form.Action.Load = function(form, options){
6992     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6993     this.reader = this.form.reader;
6994 };
6995
6996 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6997     type : 'load',
6998
6999     run : function(){
7000         
7001         Roo.Ajax.request(Roo.apply(
7002                 this.createCallback(), {
7003                     method:this.getMethod(),
7004                     url:this.getUrl(false),
7005                     params:this.getParams()
7006         }));
7007     },
7008
7009     success : function(response){
7010         
7011         var result = this.processResponse(response);
7012         if(result === true || !result.success || !result.data){
7013             this.failureType = Roo.form.Action.LOAD_FAILURE;
7014             this.form.afterAction(this, false);
7015             return;
7016         }
7017         this.form.clearInvalid();
7018         this.form.setValues(result.data);
7019         this.form.afterAction(this, true);
7020     },
7021
7022     handleResponse : function(response){
7023         if(this.form.reader){
7024             var rs = this.form.reader.read(response);
7025             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7026             return {
7027                 success : rs.success,
7028                 data : data
7029             };
7030         }
7031         return Roo.decode(response.responseText);
7032     }
7033 });
7034
7035 Roo.form.Action.ACTION_TYPES = {
7036     'load' : Roo.form.Action.Load,
7037     'submit' : Roo.form.Action.Submit
7038 };/*
7039  * - LGPL
7040  *
7041  * form
7042  * 
7043  */
7044
7045 /**
7046  * @class Roo.bootstrap.Form
7047  * @extends Roo.bootstrap.Component
7048  * Bootstrap Form class
7049  * @cfg {String} method  GET | POST (default POST)
7050  * @cfg {String} labelAlign top | left (default top)
7051  * @cfg {String} align left  | right - for navbars
7052  * @cfg {Boolean} loadMask load mask when submit (default true)
7053
7054  * 
7055  * @constructor
7056  * Create a new Form
7057  * @param {Object} config The config object
7058  */
7059
7060
7061 Roo.bootstrap.Form = function(config){
7062     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7063     this.addEvents({
7064         /**
7065          * @event clientvalidation
7066          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7067          * @param {Form} this
7068          * @param {Boolean} valid true if the form has passed client-side validation
7069          */
7070         clientvalidation: true,
7071         /**
7072          * @event beforeaction
7073          * Fires before any action is performed. Return false to cancel the action.
7074          * @param {Form} this
7075          * @param {Action} action The action to be performed
7076          */
7077         beforeaction: true,
7078         /**
7079          * @event actionfailed
7080          * Fires when an action fails.
7081          * @param {Form} this
7082          * @param {Action} action The action that failed
7083          */
7084         actionfailed : true,
7085         /**
7086          * @event actioncomplete
7087          * Fires when an action is completed.
7088          * @param {Form} this
7089          * @param {Action} action The action that completed
7090          */
7091         actioncomplete : true
7092     });
7093     
7094 };
7095
7096 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7097       
7098      /**
7099      * @cfg {String} method
7100      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7101      */
7102     method : 'POST',
7103     /**
7104      * @cfg {String} url
7105      * The URL to use for form actions if one isn't supplied in the action options.
7106      */
7107     /**
7108      * @cfg {Boolean} fileUpload
7109      * Set to true if this form is a file upload.
7110      */
7111      
7112     /**
7113      * @cfg {Object} baseParams
7114      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7115      */
7116       
7117     /**
7118      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7119      */
7120     timeout: 30,
7121     /**
7122      * @cfg {Sting} align (left|right) for navbar forms
7123      */
7124     align : 'left',
7125
7126     // private
7127     activeAction : null,
7128  
7129     /**
7130      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7131      * element by passing it or its id or mask the form itself by passing in true.
7132      * @type Mixed
7133      */
7134     waitMsgTarget : false,
7135     
7136     loadMask : true,
7137     
7138     getAutoCreate : function(){
7139         
7140         var cfg = {
7141             tag: 'form',
7142             method : this.method || 'POST',
7143             id : this.id || Roo.id(),
7144             cls : ''
7145         };
7146         if (this.parent().xtype.match(/^Nav/)) {
7147             cfg.cls = 'navbar-form navbar-' + this.align;
7148             
7149         }
7150         
7151         if (this.labelAlign == 'left' ) {
7152             cfg.cls += ' form-horizontal';
7153         }
7154         
7155         
7156         return cfg;
7157     },
7158     initEvents : function()
7159     {
7160         this.el.on('submit', this.onSubmit, this);
7161         // this was added as random key presses on the form where triggering form submit.
7162         this.el.on('keypress', function(e) {
7163             if (e.getCharCode() != 13) {
7164                 return true;
7165             }
7166             // we might need to allow it for textareas.. and some other items.
7167             // check e.getTarget().
7168             
7169             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7170                 return true;
7171             }
7172         
7173             Roo.log("keypress blocked");
7174             
7175             e.preventDefault();
7176             return false;
7177         });
7178         
7179     },
7180     // private
7181     onSubmit : function(e){
7182         e.stopEvent();
7183     },
7184     
7185      /**
7186      * Returns true if client-side validation on the form is successful.
7187      * @return Boolean
7188      */
7189     isValid : function(){
7190         var items = this.getItems();
7191         var valid = true;
7192         items.each(function(f){
7193            if(!f.validate()){
7194                valid = false;
7195                
7196            }
7197         });
7198         return valid;
7199     },
7200     /**
7201      * Returns true if any fields in this form have changed since their original load.
7202      * @return Boolean
7203      */
7204     isDirty : function(){
7205         var dirty = false;
7206         var items = this.getItems();
7207         items.each(function(f){
7208            if(f.isDirty()){
7209                dirty = true;
7210                return false;
7211            }
7212            return true;
7213         });
7214         return dirty;
7215     },
7216      /**
7217      * Performs a predefined action (submit or load) or custom actions you define on this form.
7218      * @param {String} actionName The name of the action type
7219      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7220      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7221      * accept other config options):
7222      * <pre>
7223 Property          Type             Description
7224 ----------------  ---------------  ----------------------------------------------------------------------------------
7225 url               String           The url for the action (defaults to the form's url)
7226 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7227 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7228 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7229                                    validate the form on the client (defaults to false)
7230      * </pre>
7231      * @return {BasicForm} this
7232      */
7233     doAction : function(action, options){
7234         if(typeof action == 'string'){
7235             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7236         }
7237         if(this.fireEvent('beforeaction', this, action) !== false){
7238             this.beforeAction(action);
7239             action.run.defer(100, action);
7240         }
7241         return this;
7242     },
7243     
7244     // private
7245     beforeAction : function(action){
7246         var o = action.options;
7247         
7248         if(this.loadMask){
7249             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7250         }
7251         // not really supported yet.. ??
7252         
7253         //if(this.waitMsgTarget === true){
7254         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7255         //}else if(this.waitMsgTarget){
7256         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7257         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7258         //}else {
7259         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7260        // }
7261          
7262     },
7263
7264     // private
7265     afterAction : function(action, success){
7266         this.activeAction = null;
7267         var o = action.options;
7268         
7269         //if(this.waitMsgTarget === true){
7270             this.el.unmask();
7271         //}else if(this.waitMsgTarget){
7272         //    this.waitMsgTarget.unmask();
7273         //}else{
7274         //    Roo.MessageBox.updateProgress(1);
7275         //    Roo.MessageBox.hide();
7276        // }
7277         // 
7278         if(success){
7279             if(o.reset){
7280                 this.reset();
7281             }
7282             Roo.callback(o.success, o.scope, [this, action]);
7283             this.fireEvent('actioncomplete', this, action);
7284             
7285         }else{
7286             
7287             // failure condition..
7288             // we have a scenario where updates need confirming.
7289             // eg. if a locking scenario exists..
7290             // we look for { errors : { needs_confirm : true }} in the response.
7291             if (
7292                 (typeof(action.result) != 'undefined')  &&
7293                 (typeof(action.result.errors) != 'undefined')  &&
7294                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7295            ){
7296                 var _t = this;
7297                 Roo.log("not supported yet");
7298                  /*
7299                 
7300                 Roo.MessageBox.confirm(
7301                     "Change requires confirmation",
7302                     action.result.errorMsg,
7303                     function(r) {
7304                         if (r != 'yes') {
7305                             return;
7306                         }
7307                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7308                     }
7309                     
7310                 );
7311                 */
7312                 
7313                 
7314                 return;
7315             }
7316             
7317             Roo.callback(o.failure, o.scope, [this, action]);
7318             // show an error message if no failed handler is set..
7319             if (!this.hasListener('actionfailed')) {
7320                 Roo.log("need to add dialog support");
7321                 /*
7322                 Roo.MessageBox.alert("Error",
7323                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7324                         action.result.errorMsg :
7325                         "Saving Failed, please check your entries or try again"
7326                 );
7327                 */
7328             }
7329             
7330             this.fireEvent('actionfailed', this, action);
7331         }
7332         
7333     },
7334     /**
7335      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7336      * @param {String} id The value to search for
7337      * @return Field
7338      */
7339     findField : function(id){
7340         var items = this.getItems();
7341         var field = items.get(id);
7342         if(!field){
7343              items.each(function(f){
7344                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7345                     field = f;
7346                     return false;
7347                 }
7348                 return true;
7349             });
7350         }
7351         return field || null;
7352     },
7353      /**
7354      * Mark fields in this form invalid in bulk.
7355      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7356      * @return {BasicForm} this
7357      */
7358     markInvalid : function(errors){
7359         if(errors instanceof Array){
7360             for(var i = 0, len = errors.length; i < len; i++){
7361                 var fieldError = errors[i];
7362                 var f = this.findField(fieldError.id);
7363                 if(f){
7364                     f.markInvalid(fieldError.msg);
7365                 }
7366             }
7367         }else{
7368             var field, id;
7369             for(id in errors){
7370                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7371                     field.markInvalid(errors[id]);
7372                 }
7373             }
7374         }
7375         //Roo.each(this.childForms || [], function (f) {
7376         //    f.markInvalid(errors);
7377         //});
7378         
7379         return this;
7380     },
7381
7382     /**
7383      * Set values for fields in this form in bulk.
7384      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7385      * @return {BasicForm} this
7386      */
7387     setValues : function(values){
7388         if(values instanceof Array){ // array of objects
7389             for(var i = 0, len = values.length; i < len; i++){
7390                 var v = values[i];
7391                 var f = this.findField(v.id);
7392                 if(f){
7393                     f.setValue(v.value);
7394                     if(this.trackResetOnLoad){
7395                         f.originalValue = f.getValue();
7396                     }
7397                 }
7398             }
7399         }else{ // object hash
7400             var field, id;
7401             for(id in values){
7402                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7403                     
7404                     if (field.setFromData && 
7405                         field.valueField && 
7406                         field.displayField &&
7407                         // combos' with local stores can 
7408                         // be queried via setValue()
7409                         // to set their value..
7410                         (field.store && !field.store.isLocal)
7411                         ) {
7412                         // it's a combo
7413                         var sd = { };
7414                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7415                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7416                         field.setFromData(sd);
7417                         
7418                     } else {
7419                         field.setValue(values[id]);
7420                     }
7421                     
7422                     
7423                     if(this.trackResetOnLoad){
7424                         field.originalValue = field.getValue();
7425                     }
7426                 }
7427             }
7428         }
7429          
7430         //Roo.each(this.childForms || [], function (f) {
7431         //    f.setValues(values);
7432         //});
7433                 
7434         return this;
7435     },
7436
7437     /**
7438      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7439      * they are returned as an array.
7440      * @param {Boolean} asString
7441      * @return {Object}
7442      */
7443     getValues : function(asString){
7444         //if (this.childForms) {
7445             // copy values from the child forms
7446         //    Roo.each(this.childForms, function (f) {
7447         //        this.setValues(f.getValues());
7448         //    }, this);
7449         //}
7450         
7451         
7452         
7453         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7454         if(asString === true){
7455             return fs;
7456         }
7457         return Roo.urlDecode(fs);
7458     },
7459     
7460     /**
7461      * Returns the fields in this form as an object with key/value pairs. 
7462      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7463      * @return {Object}
7464      */
7465     getFieldValues : function(with_hidden)
7466     {
7467         var items = this.getItems();
7468         var ret = {};
7469         items.each(function(f){
7470             if (!f.getName()) {
7471                 return;
7472             }
7473             var v = f.getValue();
7474             if (f.inputType =='radio') {
7475                 if (typeof(ret[f.getName()]) == 'undefined') {
7476                     ret[f.getName()] = ''; // empty..
7477                 }
7478                 
7479                 if (!f.el.dom.checked) {
7480                     return;
7481                     
7482                 }
7483                 v = f.el.dom.value;
7484                 
7485             }
7486             
7487             // not sure if this supported any more..
7488             if ((typeof(v) == 'object') && f.getRawValue) {
7489                 v = f.getRawValue() ; // dates..
7490             }
7491             // combo boxes where name != hiddenName...
7492             if (f.name != f.getName()) {
7493                 ret[f.name] = f.getRawValue();
7494             }
7495             ret[f.getName()] = v;
7496         });
7497         
7498         return ret;
7499     },
7500
7501     /**
7502      * Clears all invalid messages in this form.
7503      * @return {BasicForm} this
7504      */
7505     clearInvalid : function(){
7506         var items = this.getItems();
7507         
7508         items.each(function(f){
7509            f.clearInvalid();
7510         });
7511         
7512         
7513         
7514         return this;
7515     },
7516
7517     /**
7518      * Resets this form.
7519      * @return {BasicForm} this
7520      */
7521     reset : function(){
7522         var items = this.getItems();
7523         items.each(function(f){
7524             f.reset();
7525         });
7526         
7527         Roo.each(this.childForms || [], function (f) {
7528             f.reset();
7529         });
7530        
7531         
7532         return this;
7533     },
7534     getItems : function()
7535     {
7536         var r=new Roo.util.MixedCollection(false, function(o){
7537             return o.id || (o.id = Roo.id());
7538         });
7539         var iter = function(el) {
7540             if (el.inputEl) {
7541                 r.add(el);
7542             }
7543             if (!el.items) {
7544                 return;
7545             }
7546             Roo.each(el.items,function(e) {
7547                 iter(e);
7548             });
7549             
7550             
7551         };
7552         
7553         iter(this);
7554         return r;
7555         
7556         
7557         
7558         
7559     }
7560     
7561 });
7562
7563  
7564 /*
7565  * Based on:
7566  * Ext JS Library 1.1.1
7567  * Copyright(c) 2006-2007, Ext JS, LLC.
7568  *
7569  * Originally Released Under LGPL - original licence link has changed is not relivant.
7570  *
7571  * Fork - LGPL
7572  * <script type="text/javascript">
7573  */
7574 /**
7575  * @class Roo.form.VTypes
7576  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7577  * @singleton
7578  */
7579 Roo.form.VTypes = function(){
7580     // closure these in so they are only created once.
7581     var alpha = /^[a-zA-Z_]+$/;
7582     var alphanum = /^[a-zA-Z0-9_]+$/;
7583     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7584     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7585
7586     // All these messages and functions are configurable
7587     return {
7588         /**
7589          * The function used to validate email addresses
7590          * @param {String} value The email address
7591          */
7592         'email' : function(v){
7593             return email.test(v);
7594         },
7595         /**
7596          * The error text to display when the email validation function returns false
7597          * @type String
7598          */
7599         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7600         /**
7601          * The keystroke filter mask to be applied on email input
7602          * @type RegExp
7603          */
7604         'emailMask' : /[a-z0-9_\.\-@]/i,
7605
7606         /**
7607          * The function used to validate URLs
7608          * @param {String} value The URL
7609          */
7610         'url' : function(v){
7611             return url.test(v);
7612         },
7613         /**
7614          * The error text to display when the url validation function returns false
7615          * @type String
7616          */
7617         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7618         
7619         /**
7620          * The function used to validate alpha values
7621          * @param {String} value The value
7622          */
7623         'alpha' : function(v){
7624             return alpha.test(v);
7625         },
7626         /**
7627          * The error text to display when the alpha validation function returns false
7628          * @type String
7629          */
7630         'alphaText' : 'This field should only contain letters and _',
7631         /**
7632          * The keystroke filter mask to be applied on alpha input
7633          * @type RegExp
7634          */
7635         'alphaMask' : /[a-z_]/i,
7636
7637         /**
7638          * The function used to validate alphanumeric values
7639          * @param {String} value The value
7640          */
7641         'alphanum' : function(v){
7642             return alphanum.test(v);
7643         },
7644         /**
7645          * The error text to display when the alphanumeric validation function returns false
7646          * @type String
7647          */
7648         'alphanumText' : 'This field should only contain letters, numbers and _',
7649         /**
7650          * The keystroke filter mask to be applied on alphanumeric input
7651          * @type RegExp
7652          */
7653         'alphanumMask' : /[a-z0-9_]/i
7654     };
7655 }();/*
7656  * - LGPL
7657  *
7658  * Input
7659  * 
7660  */
7661
7662 /**
7663  * @class Roo.bootstrap.Input
7664  * @extends Roo.bootstrap.Component
7665  * Bootstrap Input class
7666  * @cfg {Boolean} disabled is it disabled
7667  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7668  * @cfg {String} name name of the input
7669  * @cfg {string} fieldLabel - the label associated
7670  * @cfg {string} placeholder - placeholder to put in text.
7671  * @cfg {string}  before - input group add on before
7672  * @cfg {string} after - input group add on after
7673  * @cfg {string} size - (lg|sm) or leave empty..
7674  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7675  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7676  * @cfg {Number} md colspan out of 12 for computer-sized screens
7677  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7678  * @cfg {string} value default value of the input
7679  * @cfg {Number} labelWidth set the width of label (0-12)
7680  * @cfg {String} labelAlign (top|left)
7681  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7682  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7683
7684  * @cfg {String} align (left|center|right) Default left
7685  * @cfg {Boolean} forceFeedback (true|false) Default false
7686  * 
7687  * 
7688  * 
7689  * 
7690  * @constructor
7691  * Create a new Input
7692  * @param {Object} config The config object
7693  */
7694
7695 Roo.bootstrap.Input = function(config){
7696     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7697    
7698         this.addEvents({
7699             /**
7700              * @event focus
7701              * Fires when this field receives input focus.
7702              * @param {Roo.form.Field} this
7703              */
7704             focus : true,
7705             /**
7706              * @event blur
7707              * Fires when this field loses input focus.
7708              * @param {Roo.form.Field} this
7709              */
7710             blur : true,
7711             /**
7712              * @event specialkey
7713              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7714              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7715              * @param {Roo.form.Field} this
7716              * @param {Roo.EventObject} e The event object
7717              */
7718             specialkey : true,
7719             /**
7720              * @event change
7721              * Fires just before the field blurs if the field value has changed.
7722              * @param {Roo.form.Field} this
7723              * @param {Mixed} newValue The new value
7724              * @param {Mixed} oldValue The original value
7725              */
7726             change : true,
7727             /**
7728              * @event invalid
7729              * Fires after the field has been marked as invalid.
7730              * @param {Roo.form.Field} this
7731              * @param {String} msg The validation message
7732              */
7733             invalid : true,
7734             /**
7735              * @event valid
7736              * Fires after the field has been validated with no errors.
7737              * @param {Roo.form.Field} this
7738              */
7739             valid : true,
7740              /**
7741              * @event keyup
7742              * Fires after the key up
7743              * @param {Roo.form.Field} this
7744              * @param {Roo.EventObject}  e The event Object
7745              */
7746             keyup : true
7747         });
7748 };
7749
7750 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7751      /**
7752      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7753       automatic validation (defaults to "keyup").
7754      */
7755     validationEvent : "keyup",
7756      /**
7757      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7758      */
7759     validateOnBlur : true,
7760     /**
7761      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7762      */
7763     validationDelay : 250,
7764      /**
7765      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7766      */
7767     focusClass : "x-form-focus",  // not needed???
7768     
7769        
7770     /**
7771      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7772      */
7773     invalidClass : "has-warning",
7774     
7775     /**
7776      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7777      */
7778     validClass : "has-success",
7779     
7780     /**
7781      * @cfg {Boolean} hasFeedback (true|false) default true
7782      */
7783     hasFeedback : true,
7784     
7785     /**
7786      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7787      */
7788     invalidFeedbackClass : "glyphicon-warning-sign",
7789     
7790     /**
7791      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7792      */
7793     validFeedbackClass : "glyphicon-ok",
7794     
7795     /**
7796      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7797      */
7798     selectOnFocus : false,
7799     
7800      /**
7801      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7802      */
7803     maskRe : null,
7804        /**
7805      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7806      */
7807     vtype : null,
7808     
7809       /**
7810      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7811      */
7812     disableKeyFilter : false,
7813     
7814        /**
7815      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7816      */
7817     disabled : false,
7818      /**
7819      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7820      */
7821     allowBlank : true,
7822     /**
7823      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7824      */
7825     blankText : "This field is required",
7826     
7827      /**
7828      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7829      */
7830     minLength : 0,
7831     /**
7832      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7833      */
7834     maxLength : Number.MAX_VALUE,
7835     /**
7836      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7837      */
7838     minLengthText : "The minimum length for this field is {0}",
7839     /**
7840      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7841      */
7842     maxLengthText : "The maximum length for this field is {0}",
7843   
7844     
7845     /**
7846      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7847      * If available, this function will be called only after the basic validators all return true, and will be passed the
7848      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7849      */
7850     validator : null,
7851     /**
7852      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7853      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7854      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7855      */
7856     regex : null,
7857     /**
7858      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7859      */
7860     regexText : "",
7861     
7862     autocomplete: false,
7863     
7864     
7865     fieldLabel : '',
7866     inputType : 'text',
7867     
7868     name : false,
7869     placeholder: false,
7870     before : false,
7871     after : false,
7872     size : false,
7873     hasFocus : false,
7874     preventMark: false,
7875     isFormField : true,
7876     value : '',
7877     labelWidth : 2,
7878     labelAlign : false,
7879     readOnly : false,
7880     align : false,
7881     formatedValue : false,
7882     forceFeedback : false,
7883     
7884     parentLabelAlign : function()
7885     {
7886         var parent = this;
7887         while (parent.parent()) {
7888             parent = parent.parent();
7889             if (typeof(parent.labelAlign) !='undefined') {
7890                 return parent.labelAlign;
7891             }
7892         }
7893         return 'left';
7894         
7895     },
7896     
7897     getAutoCreate : function(){
7898         
7899         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7900         
7901         var id = Roo.id();
7902         
7903         var cfg = {};
7904         
7905         if(this.inputType != 'hidden'){
7906             cfg.cls = 'form-group' //input-group
7907         }
7908         
7909         var input =  {
7910             tag: 'input',
7911             id : id,
7912             type : this.inputType,
7913             value : this.value,
7914             cls : 'form-control',
7915             placeholder : this.placeholder || '',
7916             autocomplete : this.autocomplete || 'new-password'
7917         };
7918         
7919         
7920         if(this.align){
7921             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7922         }
7923         
7924         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7925             input.maxLength = this.maxLength;
7926         }
7927         
7928         if (this.disabled) {
7929             input.disabled=true;
7930         }
7931         
7932         if (this.readOnly) {
7933             input.readonly=true;
7934         }
7935         
7936         if (this.name) {
7937             input.name = this.name;
7938         }
7939         if (this.size) {
7940             input.cls += ' input-' + this.size;
7941         }
7942         var settings=this;
7943         ['xs','sm','md','lg'].map(function(size){
7944             if (settings[size]) {
7945                 cfg.cls += ' col-' + size + '-' + settings[size];
7946             }
7947         });
7948         
7949         var inputblock = input;
7950         
7951         var feedback = {
7952             tag: 'span',
7953             cls: 'glyphicon form-control-feedback'
7954         };
7955             
7956         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
7957             
7958             inputblock = {
7959                 cls : 'has-feedback',
7960                 cn :  [
7961                     input,
7962                     feedback
7963                 ] 
7964             };  
7965         }
7966         
7967         if (this.before || this.after) {
7968             
7969             inputblock = {
7970                 cls : 'input-group',
7971                 cn :  [] 
7972             };
7973             
7974             if (this.before && typeof(this.before) == 'string') {
7975                 
7976                 inputblock.cn.push({
7977                     tag :'span',
7978                     cls : 'roo-input-before input-group-addon',
7979                     html : this.before
7980                 });
7981             }
7982             if (this.before && typeof(this.before) == 'object') {
7983                 this.before = Roo.factory(this.before);
7984                 
7985                 inputblock.cn.push({
7986                     tag :'span',
7987                     cls : 'roo-input-before input-group-' +
7988                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
7989                 });
7990             }
7991             
7992             inputblock.cn.push(input);
7993             
7994             if (this.after && typeof(this.after) == 'string') {
7995                 inputblock.cn.push({
7996                     tag :'span',
7997                     cls : 'roo-input-after input-group-addon',
7998                     html : this.after
7999                 });
8000             }
8001             if (this.after && typeof(this.after) == 'object') {
8002                 this.after = Roo.factory(this.after);
8003                 
8004                 inputblock.cn.push({
8005                     tag :'span',
8006                     cls : 'roo-input-after input-group-' +
8007                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8008                 });
8009             }
8010             
8011             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8012                 inputblock.cls += ' has-feedback';
8013                 inputblock.cn.push(feedback);
8014             }
8015         };
8016         
8017         if (align ==='left' && this.fieldLabel.length) {
8018                 
8019                 cfg.cn = [
8020                     
8021                     {
8022                         tag: 'label',
8023                         'for' :  id,
8024                         cls : 'control-label col-sm-' + this.labelWidth,
8025                         html : this.fieldLabel
8026                         
8027                     },
8028                     {
8029                         cls : "col-sm-" + (12 - this.labelWidth), 
8030                         cn: [
8031                             inputblock
8032                         ]
8033                     }
8034                     
8035                 ];
8036         } else if ( this.fieldLabel.length) {
8037                 
8038                  cfg.cn = [
8039                    
8040                     {
8041                         tag: 'label',
8042                         //cls : 'input-group-addon',
8043                         html : this.fieldLabel
8044                         
8045                     },
8046                     
8047                     inputblock
8048                     
8049                 ];
8050
8051         } else {
8052             
8053                 cfg.cn = [
8054                     
8055                         inputblock
8056                     
8057                 ];
8058                 
8059                 
8060         };
8061         
8062         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8063            cfg.cls += ' navbar-form';
8064         }
8065         
8066         return cfg;
8067         
8068     },
8069     /**
8070      * return the real input element.
8071      */
8072     inputEl: function ()
8073     {
8074         return this.el.select('input.form-control',true).first();
8075     },
8076     
8077     tooltipEl : function()
8078     {
8079         return this.inputEl();
8080     },
8081     
8082     setDisabled : function(v)
8083     {
8084         var i  = this.inputEl().dom;
8085         if (!v) {
8086             i.removeAttribute('disabled');
8087             return;
8088             
8089         }
8090         i.setAttribute('disabled','true');
8091     },
8092     initEvents : function()
8093     {
8094           
8095         this.inputEl().on("keydown" , this.fireKey,  this);
8096         this.inputEl().on("focus", this.onFocus,  this);
8097         this.inputEl().on("blur", this.onBlur,  this);
8098         
8099         this.inputEl().relayEvent('keyup', this);
8100  
8101         // reference to original value for reset
8102         this.originalValue = this.getValue();
8103         //Roo.form.TextField.superclass.initEvents.call(this);
8104         if(this.validationEvent == 'keyup'){
8105             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8106             this.inputEl().on('keyup', this.filterValidation, this);
8107         }
8108         else if(this.validationEvent !== false){
8109             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8110         }
8111         
8112         if(this.selectOnFocus){
8113             this.on("focus", this.preFocus, this);
8114             
8115         }
8116         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8117             this.inputEl().on("keypress", this.filterKeys, this);
8118         }
8119        /* if(this.grow){
8120             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8121             this.el.on("click", this.autoSize,  this);
8122         }
8123         */
8124         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8125             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8126         }
8127         
8128         if (typeof(this.before) == 'object') {
8129             this.before.render(this.el.select('.roo-input-before',true).first());
8130         }
8131         if (typeof(this.after) == 'object') {
8132             this.after.render(this.el.select('.roo-input-after',true).first());
8133         }
8134         
8135         
8136     },
8137     filterValidation : function(e){
8138         if(!e.isNavKeyPress()){
8139             this.validationTask.delay(this.validationDelay);
8140         }
8141     },
8142      /**
8143      * Validates the field value
8144      * @return {Boolean} True if the value is valid, else false
8145      */
8146     validate : function(){
8147         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8148         if(this.disabled || this.validateValue(this.getRawValue())){
8149             this.markValid();
8150             return true;
8151         }
8152         
8153         this.markInvalid();
8154         return false;
8155     },
8156     
8157     
8158     /**
8159      * Validates a value according to the field's validation rules and marks the field as invalid
8160      * if the validation fails
8161      * @param {Mixed} value The value to validate
8162      * @return {Boolean} True if the value is valid, else false
8163      */
8164     validateValue : function(value){
8165         if(value.length < 1)  { // if it's blank
8166             if(this.allowBlank){
8167                 return true;
8168             }
8169             return false;
8170         }
8171         
8172         if(value.length < this.minLength){
8173             return false;
8174         }
8175         if(value.length > this.maxLength){
8176             return false;
8177         }
8178         if(this.vtype){
8179             var vt = Roo.form.VTypes;
8180             if(!vt[this.vtype](value, this)){
8181                 return false;
8182             }
8183         }
8184         if(typeof this.validator == "function"){
8185             var msg = this.validator(value);
8186             if(msg !== true){
8187                 return false;
8188             }
8189         }
8190         
8191         if(this.regex && !this.regex.test(value)){
8192             return false;
8193         }
8194         
8195         return true;
8196     },
8197
8198     
8199     
8200      // private
8201     fireKey : function(e){
8202         //Roo.log('field ' + e.getKey());
8203         if(e.isNavKeyPress()){
8204             this.fireEvent("specialkey", this, e);
8205         }
8206     },
8207     focus : function (selectText){
8208         if(this.rendered){
8209             this.inputEl().focus();
8210             if(selectText === true){
8211                 this.inputEl().dom.select();
8212             }
8213         }
8214         return this;
8215     } ,
8216     
8217     onFocus : function(){
8218         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8219            // this.el.addClass(this.focusClass);
8220         }
8221         if(!this.hasFocus){
8222             this.hasFocus = true;
8223             this.startValue = this.getValue();
8224             this.fireEvent("focus", this);
8225         }
8226     },
8227     
8228     beforeBlur : Roo.emptyFn,
8229
8230     
8231     // private
8232     onBlur : function(){
8233         this.beforeBlur();
8234         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8235             //this.el.removeClass(this.focusClass);
8236         }
8237         this.hasFocus = false;
8238         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8239             this.validate();
8240         }
8241         var v = this.getValue();
8242         if(String(v) !== String(this.startValue)){
8243             this.fireEvent('change', this, v, this.startValue);
8244         }
8245         this.fireEvent("blur", this);
8246     },
8247     
8248     /**
8249      * Resets the current field value to the originally loaded value and clears any validation messages
8250      */
8251     reset : function(){
8252         this.setValue(this.originalValue);
8253         this.validate();
8254     },
8255      /**
8256      * Returns the name of the field
8257      * @return {Mixed} name The name field
8258      */
8259     getName: function(){
8260         return this.name;
8261     },
8262      /**
8263      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8264      * @return {Mixed} value The field value
8265      */
8266     getValue : function(){
8267         
8268         var v = this.inputEl().getValue();
8269         
8270         return v;
8271     },
8272     /**
8273      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8274      * @return {Mixed} value The field value
8275      */
8276     getRawValue : function(){
8277         var v = this.inputEl().getValue();
8278         
8279         return v;
8280     },
8281     
8282     /**
8283      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8284      * @param {Mixed} value The value to set
8285      */
8286     setRawValue : function(v){
8287         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8288     },
8289     
8290     selectText : function(start, end){
8291         var v = this.getRawValue();
8292         if(v.length > 0){
8293             start = start === undefined ? 0 : start;
8294             end = end === undefined ? v.length : end;
8295             var d = this.inputEl().dom;
8296             if(d.setSelectionRange){
8297                 d.setSelectionRange(start, end);
8298             }else if(d.createTextRange){
8299                 var range = d.createTextRange();
8300                 range.moveStart("character", start);
8301                 range.moveEnd("character", v.length-end);
8302                 range.select();
8303             }
8304         }
8305     },
8306     
8307     /**
8308      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8309      * @param {Mixed} value The value to set
8310      */
8311     setValue : function(v){
8312         this.value = v;
8313         if(this.rendered){
8314             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8315             this.validate();
8316         }
8317     },
8318     
8319     /*
8320     processValue : function(value){
8321         if(this.stripCharsRe){
8322             var newValue = value.replace(this.stripCharsRe, '');
8323             if(newValue !== value){
8324                 this.setRawValue(newValue);
8325                 return newValue;
8326             }
8327         }
8328         return value;
8329     },
8330   */
8331     preFocus : function(){
8332         
8333         if(this.selectOnFocus){
8334             this.inputEl().dom.select();
8335         }
8336     },
8337     filterKeys : function(e){
8338         var k = e.getKey();
8339         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8340             return;
8341         }
8342         var c = e.getCharCode(), cc = String.fromCharCode(c);
8343         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8344             return;
8345         }
8346         if(!this.maskRe.test(cc)){
8347             e.stopEvent();
8348         }
8349     },
8350      /**
8351      * Clear any invalid styles/messages for this field
8352      */
8353     clearInvalid : function(){
8354         
8355         if(!this.el || this.preventMark){ // not rendered
8356             return;
8357         }
8358         
8359         var label = this.el.select('label', true).first();
8360         var icon = this.el.select('i.fa-star', true).first();
8361         
8362         if(label && icon){
8363             icon.remove();
8364         }
8365         
8366         this.el.removeClass(this.invalidClass);
8367         
8368         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8369             
8370             var feedback = this.el.select('.form-control-feedback', true).first();
8371             
8372             if(feedback){
8373                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8374             }
8375             
8376         }
8377         
8378         this.fireEvent('valid', this);
8379     },
8380     
8381      /**
8382      * Mark this field as valid
8383      */
8384     markValid : function()
8385     {
8386         if(!this.el  || this.preventMark){ // not rendered
8387             return;
8388         }
8389         
8390         this.el.removeClass([this.invalidClass, this.validClass]);
8391         
8392         var feedback = this.el.select('.form-control-feedback', true).first();
8393             
8394         if(feedback){
8395             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8396         }
8397
8398         if(this.disabled || this.allowBlank){
8399             return;
8400         }
8401         
8402         var formGroup = this.el.findParent('.form-group', false, true);
8403         
8404         if(formGroup){
8405             
8406             var label = formGroup.select('label', true).first();
8407             var icon = formGroup.select('i.fa-star', true).first();
8408             
8409             if(label && icon){
8410                 icon.remove();
8411             }
8412         }
8413         
8414         this.el.addClass(this.validClass);
8415         
8416         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8417             
8418             var feedback = this.el.select('.form-control-feedback', true).first();
8419             
8420             if(feedback){
8421                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8422                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8423             }
8424             
8425         }
8426         
8427         this.fireEvent('valid', this);
8428     },
8429     
8430      /**
8431      * Mark this field as invalid
8432      * @param {String} msg The validation message
8433      */
8434     markInvalid : function(msg)
8435     {
8436         if(!this.el  || this.preventMark){ // not rendered
8437             return;
8438         }
8439         
8440         this.el.removeClass([this.invalidClass, this.validClass]);
8441         
8442         var feedback = this.el.select('.form-control-feedback', true).first();
8443             
8444         if(feedback){
8445             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8446         }
8447
8448         if(this.disabled || this.allowBlank){
8449             return;
8450         }
8451         
8452         var formGroup = this.el.findParent('.form-group', false, true);
8453         
8454         if(formGroup){
8455             var label = formGroup.select('label', true).first();
8456             var icon = formGroup.select('i.fa-star', true).first();
8457
8458             if(!this.getValue().length && label && !icon){
8459                 this.el.findParent('.form-group', false, true).createChild({
8460                     tag : 'i',
8461                     cls : 'text-danger fa fa-lg fa-star',
8462                     tooltip : 'This field is required',
8463                     style : 'margin-right:5px;'
8464                 }, label, true);
8465             }
8466         }
8467         
8468         
8469         this.el.addClass(this.invalidClass);
8470         
8471         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8472             
8473             var feedback = this.el.select('.form-control-feedback', true).first();
8474             
8475             if(feedback){
8476                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8477                 
8478                 if(this.getValue().length || this.forceFeedback){
8479                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8480                 }
8481                 
8482             }
8483             
8484         }
8485         
8486         this.fireEvent('invalid', this, msg);
8487     },
8488     // private
8489     SafariOnKeyDown : function(event)
8490     {
8491         // this is a workaround for a password hang bug on chrome/ webkit.
8492         
8493         var isSelectAll = false;
8494         
8495         if(this.inputEl().dom.selectionEnd > 0){
8496             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8497         }
8498         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8499             event.preventDefault();
8500             this.setValue('');
8501             return;
8502         }
8503         
8504         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8505             
8506             event.preventDefault();
8507             // this is very hacky as keydown always get's upper case.
8508             //
8509             var cc = String.fromCharCode(event.getCharCode());
8510             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8511             
8512         }
8513     },
8514     adjustWidth : function(tag, w){
8515         tag = tag.toLowerCase();
8516         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8517             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8518                 if(tag == 'input'){
8519                     return w + 2;
8520                 }
8521                 if(tag == 'textarea'){
8522                     return w-2;
8523                 }
8524             }else if(Roo.isOpera){
8525                 if(tag == 'input'){
8526                     return w + 2;
8527                 }
8528                 if(tag == 'textarea'){
8529                     return w-2;
8530                 }
8531             }
8532         }
8533         return w;
8534     }
8535     
8536 });
8537
8538  
8539 /*
8540  * - LGPL
8541  *
8542  * Input
8543  * 
8544  */
8545
8546 /**
8547  * @class Roo.bootstrap.TextArea
8548  * @extends Roo.bootstrap.Input
8549  * Bootstrap TextArea class
8550  * @cfg {Number} cols Specifies the visible width of a text area
8551  * @cfg {Number} rows Specifies the visible number of lines in a text area
8552  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8553  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8554  * @cfg {string} html text
8555  * 
8556  * @constructor
8557  * Create a new TextArea
8558  * @param {Object} config The config object
8559  */
8560
8561 Roo.bootstrap.TextArea = function(config){
8562     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8563    
8564 };
8565
8566 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8567      
8568     cols : false,
8569     rows : 5,
8570     readOnly : false,
8571     warp : 'soft',
8572     resize : false,
8573     value: false,
8574     html: false,
8575     
8576     getAutoCreate : function(){
8577         
8578         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8579         
8580         var id = Roo.id();
8581         
8582         var cfg = {};
8583         
8584         var input =  {
8585             tag: 'textarea',
8586             id : id,
8587             warp : this.warp,
8588             rows : this.rows,
8589             value : this.value || '',
8590             html: this.html || '',
8591             cls : 'form-control',
8592             placeholder : this.placeholder || '' 
8593             
8594         };
8595         
8596         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8597             input.maxLength = this.maxLength;
8598         }
8599         
8600         if(this.resize){
8601             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8602         }
8603         
8604         if(this.cols){
8605             input.cols = this.cols;
8606         }
8607         
8608         if (this.readOnly) {
8609             input.readonly = true;
8610         }
8611         
8612         if (this.name) {
8613             input.name = this.name;
8614         }
8615         
8616         if (this.size) {
8617             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8618         }
8619         
8620         var settings=this;
8621         ['xs','sm','md','lg'].map(function(size){
8622             if (settings[size]) {
8623                 cfg.cls += ' col-' + size + '-' + settings[size];
8624             }
8625         });
8626         
8627         var inputblock = input;
8628         
8629         if(this.hasFeedback && !this.allowBlank){
8630             
8631             var feedback = {
8632                 tag: 'span',
8633                 cls: 'glyphicon form-control-feedback'
8634             };
8635
8636             inputblock = {
8637                 cls : 'has-feedback',
8638                 cn :  [
8639                     input,
8640                     feedback
8641                 ] 
8642             };  
8643         }
8644         
8645         
8646         if (this.before || this.after) {
8647             
8648             inputblock = {
8649                 cls : 'input-group',
8650                 cn :  [] 
8651             };
8652             if (this.before) {
8653                 inputblock.cn.push({
8654                     tag :'span',
8655                     cls : 'input-group-addon',
8656                     html : this.before
8657                 });
8658             }
8659             
8660             inputblock.cn.push(input);
8661             
8662             if(this.hasFeedback && !this.allowBlank){
8663                 inputblock.cls += ' has-feedback';
8664                 inputblock.cn.push(feedback);
8665             }
8666             
8667             if (this.after) {
8668                 inputblock.cn.push({
8669                     tag :'span',
8670                     cls : 'input-group-addon',
8671                     html : this.after
8672                 });
8673             }
8674             
8675         }
8676         
8677         if (align ==='left' && this.fieldLabel.length) {
8678 //                Roo.log("left and has label");
8679                 cfg.cn = [
8680                     
8681                     {
8682                         tag: 'label',
8683                         'for' :  id,
8684                         cls : 'control-label col-sm-' + this.labelWidth,
8685                         html : this.fieldLabel
8686                         
8687                     },
8688                     {
8689                         cls : "col-sm-" + (12 - this.labelWidth), 
8690                         cn: [
8691                             inputblock
8692                         ]
8693                     }
8694                     
8695                 ];
8696         } else if ( this.fieldLabel.length) {
8697 //                Roo.log(" label");
8698                  cfg.cn = [
8699                    
8700                     {
8701                         tag: 'label',
8702                         //cls : 'input-group-addon',
8703                         html : this.fieldLabel
8704                         
8705                     },
8706                     
8707                     inputblock
8708                     
8709                 ];
8710
8711         } else {
8712             
8713 //                   Roo.log(" no label && no align");
8714                 cfg.cn = [
8715                     
8716                         inputblock
8717                     
8718                 ];
8719                 
8720                 
8721         }
8722         
8723         if (this.disabled) {
8724             input.disabled=true;
8725         }
8726         
8727         return cfg;
8728         
8729     },
8730     /**
8731      * return the real textarea element.
8732      */
8733     inputEl: function ()
8734     {
8735         return this.el.select('textarea.form-control',true).first();
8736     },
8737     
8738     /**
8739      * Clear any invalid styles/messages for this field
8740      */
8741     clearInvalid : function()
8742     {
8743         
8744         if(!this.el || this.preventMark){ // not rendered
8745             return;
8746         }
8747         
8748         var label = this.el.select('label', true).first();
8749         var icon = this.el.select('i.fa-star', true).first();
8750         
8751         if(label && icon){
8752             icon.remove();
8753         }
8754         
8755         this.el.removeClass(this.invalidClass);
8756         
8757         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8758             
8759             var feedback = this.el.select('.form-control-feedback', true).first();
8760             
8761             if(feedback){
8762                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8763             }
8764             
8765         }
8766         
8767         this.fireEvent('valid', this);
8768     },
8769     
8770      /**
8771      * Mark this field as valid
8772      */
8773     markValid : function()
8774     {
8775         if(!this.el  || this.preventMark){ // not rendered
8776             return;
8777         }
8778         
8779         this.el.removeClass([this.invalidClass, this.validClass]);
8780         
8781         var feedback = this.el.select('.form-control-feedback', true).first();
8782             
8783         if(feedback){
8784             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8785         }
8786
8787         if(this.disabled || this.allowBlank){
8788             return;
8789         }
8790         
8791         var label = this.el.select('label', true).first();
8792         var icon = this.el.select('i.fa-star', true).first();
8793         
8794         if(label && icon){
8795             icon.remove();
8796         }
8797         
8798         this.el.addClass(this.validClass);
8799         
8800         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8801             
8802             var feedback = this.el.select('.form-control-feedback', true).first();
8803             
8804             if(feedback){
8805                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8806                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8807             }
8808             
8809         }
8810         
8811         this.fireEvent('valid', this);
8812     },
8813     
8814      /**
8815      * Mark this field as invalid
8816      * @param {String} msg The validation message
8817      */
8818     markInvalid : function(msg)
8819     {
8820         if(!this.el  || this.preventMark){ // not rendered
8821             return;
8822         }
8823         
8824         this.el.removeClass([this.invalidClass, this.validClass]);
8825         
8826         var feedback = this.el.select('.form-control-feedback', true).first();
8827             
8828         if(feedback){
8829             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8830         }
8831
8832         if(this.disabled || this.allowBlank){
8833             return;
8834         }
8835         
8836         var label = this.el.select('label', true).first();
8837         var icon = this.el.select('i.fa-star', true).first();
8838         
8839         if(!this.getValue().length && label && !icon){
8840             this.el.createChild({
8841                 tag : 'i',
8842                 cls : 'text-danger fa fa-lg fa-star',
8843                 tooltip : 'This field is required',
8844                 style : 'margin-right:5px;'
8845             }, label, true);
8846         }
8847
8848         this.el.addClass(this.invalidClass);
8849         
8850         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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                 if(this.getValue().length || this.forceFeedback){
8858                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8859                 }
8860                 
8861             }
8862             
8863         }
8864         
8865         this.fireEvent('invalid', this, msg);
8866     }
8867 });
8868
8869  
8870 /*
8871  * - LGPL
8872  *
8873  * trigger field - base class for combo..
8874  * 
8875  */
8876  
8877 /**
8878  * @class Roo.bootstrap.TriggerField
8879  * @extends Roo.bootstrap.Input
8880  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8881  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8882  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8883  * for which you can provide a custom implementation.  For example:
8884  * <pre><code>
8885 var trigger = new Roo.bootstrap.TriggerField();
8886 trigger.onTriggerClick = myTriggerFn;
8887 trigger.applyTo('my-field');
8888 </code></pre>
8889  *
8890  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8891  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8892  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8893  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8894  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8895
8896  * @constructor
8897  * Create a new TriggerField.
8898  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8899  * to the base TextField)
8900  */
8901 Roo.bootstrap.TriggerField = function(config){
8902     this.mimicing = false;
8903     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8904 };
8905
8906 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8907     /**
8908      * @cfg {String} triggerClass A CSS class to apply to the trigger
8909      */
8910      /**
8911      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8912      */
8913     hideTrigger:false,
8914
8915     /**
8916      * @cfg {Boolean} removable (true|false) special filter default false
8917      */
8918     removable : false,
8919     
8920     /** @cfg {Boolean} grow @hide */
8921     /** @cfg {Number} growMin @hide */
8922     /** @cfg {Number} growMax @hide */
8923
8924     /**
8925      * @hide 
8926      * @method
8927      */
8928     autoSize: Roo.emptyFn,
8929     // private
8930     monitorTab : true,
8931     // private
8932     deferHeight : true,
8933
8934     
8935     actionMode : 'wrap',
8936     
8937     caret : false,
8938     
8939     
8940     getAutoCreate : function(){
8941        
8942         var align = this.labelAlign || this.parentLabelAlign();
8943         
8944         var id = Roo.id();
8945         
8946         var cfg = {
8947             cls: 'form-group' //input-group
8948         };
8949         
8950         
8951         var input =  {
8952             tag: 'input',
8953             id : id,
8954             type : this.inputType,
8955             cls : 'form-control',
8956             autocomplete: 'new-password',
8957             placeholder : this.placeholder || '' 
8958             
8959         };
8960         if (this.name) {
8961             input.name = this.name;
8962         }
8963         if (this.size) {
8964             input.cls += ' input-' + this.size;
8965         }
8966         
8967         if (this.disabled) {
8968             input.disabled=true;
8969         }
8970         
8971         var inputblock = input;
8972         
8973         if(this.hasFeedback && !this.allowBlank){
8974             
8975             var feedback = {
8976                 tag: 'span',
8977                 cls: 'glyphicon form-control-feedback'
8978             };
8979             
8980             if(this.removable && !this.editable && !this.tickable){
8981                 inputblock = {
8982                     cls : 'has-feedback',
8983                     cn :  [
8984                         inputblock,
8985                         {
8986                             tag: 'button',
8987                             html : 'x',
8988                             cls : 'roo-combo-removable-btn close'
8989                         },
8990                         feedback
8991                     ] 
8992                 };
8993             } else {
8994                 inputblock = {
8995                     cls : 'has-feedback',
8996                     cn :  [
8997                         inputblock,
8998                         feedback
8999                     ] 
9000                 };
9001             }
9002
9003         } else {
9004             if(this.removable && !this.editable && !this.tickable){
9005                 inputblock = {
9006                     cls : 'roo-removable',
9007                     cn :  [
9008                         inputblock,
9009                         {
9010                             tag: 'button',
9011                             html : 'x',
9012                             cls : 'roo-combo-removable-btn close'
9013                         }
9014                     ] 
9015                 };
9016             }
9017         }
9018         
9019         if (this.before || this.after) {
9020             
9021             inputblock = {
9022                 cls : 'input-group',
9023                 cn :  [] 
9024             };
9025             if (this.before) {
9026                 inputblock.cn.push({
9027                     tag :'span',
9028                     cls : 'input-group-addon',
9029                     html : this.before
9030                 });
9031             }
9032             
9033             inputblock.cn.push(input);
9034             
9035             if(this.hasFeedback && !this.allowBlank){
9036                 inputblock.cls += ' has-feedback';
9037                 inputblock.cn.push(feedback);
9038             }
9039             
9040             if (this.after) {
9041                 inputblock.cn.push({
9042                     tag :'span',
9043                     cls : 'input-group-addon',
9044                     html : this.after
9045                 });
9046             }
9047             
9048         };
9049         
9050         var box = {
9051             tag: 'div',
9052             cn: [
9053                 {
9054                     tag: 'input',
9055                     type : 'hidden',
9056                     cls: 'form-hidden-field'
9057                 },
9058                 inputblock
9059             ]
9060             
9061         };
9062         
9063         if(this.multiple){
9064             box = {
9065                 tag: 'div',
9066                 cn: [
9067                     {
9068                         tag: 'input',
9069                         type : 'hidden',
9070                         cls: 'form-hidden-field'
9071                     },
9072                     {
9073                         tag: 'ul',
9074                         cls: 'select2-choices',
9075                         cn:[
9076                             {
9077                                 tag: 'li',
9078                                 cls: 'select2-search-field',
9079                                 cn: [
9080
9081                                     inputblock
9082                                 ]
9083                             }
9084                         ]
9085                     }
9086                 ]
9087             }
9088         };
9089         
9090         var combobox = {
9091             cls: 'select2-container input-group',
9092             cn: [
9093                 box
9094 //                {
9095 //                    tag: 'ul',
9096 //                    cls: 'typeahead typeahead-long dropdown-menu',
9097 //                    style: 'display:none'
9098 //                }
9099             ]
9100         };
9101         
9102         if(!this.multiple && this.showToggleBtn){
9103             
9104             var caret = {
9105                         tag: 'span',
9106                         cls: 'caret'
9107              };
9108             if (this.caret != false) {
9109                 caret = {
9110                      tag: 'i',
9111                      cls: 'fa fa-' + this.caret
9112                 };
9113                 
9114             }
9115             
9116             combobox.cn.push({
9117                 tag :'span',
9118                 cls : 'input-group-addon btn dropdown-toggle',
9119                 cn : [
9120                     caret,
9121                     {
9122                         tag: 'span',
9123                         cls: 'combobox-clear',
9124                         cn  : [
9125                             {
9126                                 tag : 'i',
9127                                 cls: 'icon-remove'
9128                             }
9129                         ]
9130                     }
9131                 ]
9132
9133             })
9134         }
9135         
9136         if(this.multiple){
9137             combobox.cls += ' select2-container-multi';
9138         }
9139         
9140         if (align ==='left' && this.fieldLabel.length) {
9141             
9142 //                Roo.log("left and has label");
9143                 cfg.cn = [
9144                     
9145                     {
9146                         tag: 'label',
9147                         'for' :  id,
9148                         cls : 'control-label col-sm-' + this.labelWidth,
9149                         html : this.fieldLabel
9150                         
9151                     },
9152                     {
9153                         cls : "col-sm-" + (12 - this.labelWidth), 
9154                         cn: [
9155                             combobox
9156                         ]
9157                     }
9158                     
9159                 ];
9160         } else if ( this.fieldLabel.length) {
9161 //                Roo.log(" label");
9162                  cfg.cn = [
9163                    
9164                     {
9165                         tag: 'label',
9166                         //cls : 'input-group-addon',
9167                         html : this.fieldLabel
9168                         
9169                     },
9170                     
9171                     combobox
9172                     
9173                 ];
9174
9175         } else {
9176             
9177 //                Roo.log(" no label && no align");
9178                 cfg = combobox
9179                      
9180                 
9181         }
9182          
9183         var settings=this;
9184         ['xs','sm','md','lg'].map(function(size){
9185             if (settings[size]) {
9186                 cfg.cls += ' col-' + size + '-' + settings[size];
9187             }
9188         });
9189         
9190         return cfg;
9191         
9192     },
9193     
9194     
9195     
9196     // private
9197     onResize : function(w, h){
9198 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9199 //        if(typeof w == 'number'){
9200 //            var x = w - this.trigger.getWidth();
9201 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9202 //            this.trigger.setStyle('left', x+'px');
9203 //        }
9204     },
9205
9206     // private
9207     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9208
9209     // private
9210     getResizeEl : function(){
9211         return this.inputEl();
9212     },
9213
9214     // private
9215     getPositionEl : function(){
9216         return this.inputEl();
9217     },
9218
9219     // private
9220     alignErrorIcon : function(){
9221         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9222     },
9223
9224     // private
9225     initEvents : function(){
9226         
9227         this.createList();
9228         
9229         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9230         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9231         if(!this.multiple && this.showToggleBtn){
9232             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9233             if(this.hideTrigger){
9234                 this.trigger.setDisplayed(false);
9235             }
9236             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9237         }
9238         
9239         if(this.multiple){
9240             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9241         }
9242         
9243         if(this.removable && !this.editable && !this.tickable){
9244             var close = this.closeTriggerEl();
9245             
9246             if(close){
9247                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9248                 close.on('click', this.removeBtnClick, this, close);
9249             }
9250         }
9251         
9252         //this.trigger.addClassOnOver('x-form-trigger-over');
9253         //this.trigger.addClassOnClick('x-form-trigger-click');
9254         
9255         //if(!this.width){
9256         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9257         //}
9258     },
9259     
9260     closeTriggerEl : function()
9261     {
9262         var close = this.el.select('.roo-combo-removable-btn', true).first();
9263         return close ? close : false;
9264     },
9265     
9266     removeBtnClick : function(e, h, el)
9267     {
9268         e.preventDefault();
9269         
9270         if(this.fireEvent("remove", this) !== false){
9271             this.reset();
9272         }
9273     },
9274     
9275     createList : function()
9276     {
9277         this.list = Roo.get(document.body).createChild({
9278             tag: 'ul',
9279             cls: 'typeahead typeahead-long dropdown-menu',
9280             style: 'display:none'
9281         });
9282         
9283         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9284         
9285     },
9286
9287     // private
9288     initTrigger : function(){
9289        
9290     },
9291
9292     // private
9293     onDestroy : function(){
9294         if(this.trigger){
9295             this.trigger.removeAllListeners();
9296           //  this.trigger.remove();
9297         }
9298         //if(this.wrap){
9299         //    this.wrap.remove();
9300         //}
9301         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9302     },
9303
9304     // private
9305     onFocus : function(){
9306         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9307         /*
9308         if(!this.mimicing){
9309             this.wrap.addClass('x-trigger-wrap-focus');
9310             this.mimicing = true;
9311             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9312             if(this.monitorTab){
9313                 this.el.on("keydown", this.checkTab, this);
9314             }
9315         }
9316         */
9317     },
9318
9319     // private
9320     checkTab : function(e){
9321         if(e.getKey() == e.TAB){
9322             this.triggerBlur();
9323         }
9324     },
9325
9326     // private
9327     onBlur : function(){
9328         // do nothing
9329     },
9330
9331     // private
9332     mimicBlur : function(e, t){
9333         /*
9334         if(!this.wrap.contains(t) && this.validateBlur()){
9335             this.triggerBlur();
9336         }
9337         */
9338     },
9339
9340     // private
9341     triggerBlur : function(){
9342         this.mimicing = false;
9343         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9344         if(this.monitorTab){
9345             this.el.un("keydown", this.checkTab, this);
9346         }
9347         //this.wrap.removeClass('x-trigger-wrap-focus');
9348         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9349     },
9350
9351     // private
9352     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9353     validateBlur : function(e, t){
9354         return true;
9355     },
9356
9357     // private
9358     onDisable : function(){
9359         this.inputEl().dom.disabled = true;
9360         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9361         //if(this.wrap){
9362         //    this.wrap.addClass('x-item-disabled');
9363         //}
9364     },
9365
9366     // private
9367     onEnable : function(){
9368         this.inputEl().dom.disabled = false;
9369         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9370         //if(this.wrap){
9371         //    this.el.removeClass('x-item-disabled');
9372         //}
9373     },
9374
9375     // private
9376     onShow : function(){
9377         var ae = this.getActionEl();
9378         
9379         if(ae){
9380             ae.dom.style.display = '';
9381             ae.dom.style.visibility = 'visible';
9382         }
9383     },
9384
9385     // private
9386     
9387     onHide : function(){
9388         var ae = this.getActionEl();
9389         ae.dom.style.display = 'none';
9390     },
9391
9392     /**
9393      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9394      * by an implementing function.
9395      * @method
9396      * @param {EventObject} e
9397      */
9398     onTriggerClick : Roo.emptyFn
9399 });
9400  /*
9401  * Based on:
9402  * Ext JS Library 1.1.1
9403  * Copyright(c) 2006-2007, Ext JS, LLC.
9404  *
9405  * Originally Released Under LGPL - original licence link has changed is not relivant.
9406  *
9407  * Fork - LGPL
9408  * <script type="text/javascript">
9409  */
9410
9411
9412 /**
9413  * @class Roo.data.SortTypes
9414  * @singleton
9415  * Defines the default sorting (casting?) comparison functions used when sorting data.
9416  */
9417 Roo.data.SortTypes = {
9418     /**
9419      * Default sort that does nothing
9420      * @param {Mixed} s The value being converted
9421      * @return {Mixed} The comparison value
9422      */
9423     none : function(s){
9424         return s;
9425     },
9426     
9427     /**
9428      * The regular expression used to strip tags
9429      * @type {RegExp}
9430      * @property
9431      */
9432     stripTagsRE : /<\/?[^>]+>/gi,
9433     
9434     /**
9435      * Strips all HTML tags to sort on text only
9436      * @param {Mixed} s The value being converted
9437      * @return {String} The comparison value
9438      */
9439     asText : function(s){
9440         return String(s).replace(this.stripTagsRE, "");
9441     },
9442     
9443     /**
9444      * Strips all HTML tags to sort on text only - Case insensitive
9445      * @param {Mixed} s The value being converted
9446      * @return {String} The comparison value
9447      */
9448     asUCText : function(s){
9449         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9450     },
9451     
9452     /**
9453      * Case insensitive string
9454      * @param {Mixed} s The value being converted
9455      * @return {String} The comparison value
9456      */
9457     asUCString : function(s) {
9458         return String(s).toUpperCase();
9459     },
9460     
9461     /**
9462      * Date sorting
9463      * @param {Mixed} s The value being converted
9464      * @return {Number} The comparison value
9465      */
9466     asDate : function(s) {
9467         if(!s){
9468             return 0;
9469         }
9470         if(s instanceof Date){
9471             return s.getTime();
9472         }
9473         return Date.parse(String(s));
9474     },
9475     
9476     /**
9477      * Float sorting
9478      * @param {Mixed} s The value being converted
9479      * @return {Float} The comparison value
9480      */
9481     asFloat : function(s) {
9482         var val = parseFloat(String(s).replace(/,/g, ""));
9483         if(isNaN(val)) {
9484             val = 0;
9485         }
9486         return val;
9487     },
9488     
9489     /**
9490      * Integer sorting
9491      * @param {Mixed} s The value being converted
9492      * @return {Number} The comparison value
9493      */
9494     asInt : function(s) {
9495         var val = parseInt(String(s).replace(/,/g, ""));
9496         if(isNaN(val)) {
9497             val = 0;
9498         }
9499         return val;
9500     }
9501 };/*
9502  * Based on:
9503  * Ext JS Library 1.1.1
9504  * Copyright(c) 2006-2007, Ext JS, LLC.
9505  *
9506  * Originally Released Under LGPL - original licence link has changed is not relivant.
9507  *
9508  * Fork - LGPL
9509  * <script type="text/javascript">
9510  */
9511
9512 /**
9513 * @class Roo.data.Record
9514  * Instances of this class encapsulate both record <em>definition</em> information, and record
9515  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9516  * to access Records cached in an {@link Roo.data.Store} object.<br>
9517  * <p>
9518  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9519  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9520  * objects.<br>
9521  * <p>
9522  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9523  * @constructor
9524  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9525  * {@link #create}. The parameters are the same.
9526  * @param {Array} data An associative Array of data values keyed by the field name.
9527  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9528  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9529  * not specified an integer id is generated.
9530  */
9531 Roo.data.Record = function(data, id){
9532     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9533     this.data = data;
9534 };
9535
9536 /**
9537  * Generate a constructor for a specific record layout.
9538  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9539  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9540  * Each field definition object may contain the following properties: <ul>
9541  * <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,
9542  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9543  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9544  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9545  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9546  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9547  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9548  * this may be omitted.</p></li>
9549  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9550  * <ul><li>auto (Default, implies no conversion)</li>
9551  * <li>string</li>
9552  * <li>int</li>
9553  * <li>float</li>
9554  * <li>boolean</li>
9555  * <li>date</li></ul></p></li>
9556  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9557  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9558  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9559  * by the Reader into an object that will be stored in the Record. It is passed the
9560  * following parameters:<ul>
9561  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9562  * </ul></p></li>
9563  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9564  * </ul>
9565  * <br>usage:<br><pre><code>
9566 var TopicRecord = Roo.data.Record.create(
9567     {name: 'title', mapping: 'topic_title'},
9568     {name: 'author', mapping: 'username'},
9569     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9570     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9571     {name: 'lastPoster', mapping: 'user2'},
9572     {name: 'excerpt', mapping: 'post_text'}
9573 );
9574
9575 var myNewRecord = new TopicRecord({
9576     title: 'Do my job please',
9577     author: 'noobie',
9578     totalPosts: 1,
9579     lastPost: new Date(),
9580     lastPoster: 'Animal',
9581     excerpt: 'No way dude!'
9582 });
9583 myStore.add(myNewRecord);
9584 </code></pre>
9585  * @method create
9586  * @static
9587  */
9588 Roo.data.Record.create = function(o){
9589     var f = function(){
9590         f.superclass.constructor.apply(this, arguments);
9591     };
9592     Roo.extend(f, Roo.data.Record);
9593     var p = f.prototype;
9594     p.fields = new Roo.util.MixedCollection(false, function(field){
9595         return field.name;
9596     });
9597     for(var i = 0, len = o.length; i < len; i++){
9598         p.fields.add(new Roo.data.Field(o[i]));
9599     }
9600     f.getField = function(name){
9601         return p.fields.get(name);  
9602     };
9603     return f;
9604 };
9605
9606 Roo.data.Record.AUTO_ID = 1000;
9607 Roo.data.Record.EDIT = 'edit';
9608 Roo.data.Record.REJECT = 'reject';
9609 Roo.data.Record.COMMIT = 'commit';
9610
9611 Roo.data.Record.prototype = {
9612     /**
9613      * Readonly flag - true if this record has been modified.
9614      * @type Boolean
9615      */
9616     dirty : false,
9617     editing : false,
9618     error: null,
9619     modified: null,
9620
9621     // private
9622     join : function(store){
9623         this.store = store;
9624     },
9625
9626     /**
9627      * Set the named field to the specified value.
9628      * @param {String} name The name of the field to set.
9629      * @param {Object} value The value to set the field to.
9630      */
9631     set : function(name, value){
9632         if(this.data[name] == value){
9633             return;
9634         }
9635         this.dirty = true;
9636         if(!this.modified){
9637             this.modified = {};
9638         }
9639         if(typeof this.modified[name] == 'undefined'){
9640             this.modified[name] = this.data[name];
9641         }
9642         this.data[name] = value;
9643         if(!this.editing && this.store){
9644             this.store.afterEdit(this);
9645         }       
9646     },
9647
9648     /**
9649      * Get the value of the named field.
9650      * @param {String} name The name of the field to get the value of.
9651      * @return {Object} The value of the field.
9652      */
9653     get : function(name){
9654         return this.data[name]; 
9655     },
9656
9657     // private
9658     beginEdit : function(){
9659         this.editing = true;
9660         this.modified = {}; 
9661     },
9662
9663     // private
9664     cancelEdit : function(){
9665         this.editing = false;
9666         delete this.modified;
9667     },
9668
9669     // private
9670     endEdit : function(){
9671         this.editing = false;
9672         if(this.dirty && this.store){
9673             this.store.afterEdit(this);
9674         }
9675     },
9676
9677     /**
9678      * Usually called by the {@link Roo.data.Store} which owns the Record.
9679      * Rejects all changes made to the Record since either creation, or the last commit operation.
9680      * Modified fields are reverted to their original values.
9681      * <p>
9682      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9683      * of reject operations.
9684      */
9685     reject : function(){
9686         var m = this.modified;
9687         for(var n in m){
9688             if(typeof m[n] != "function"){
9689                 this.data[n] = m[n];
9690             }
9691         }
9692         this.dirty = false;
9693         delete this.modified;
9694         this.editing = false;
9695         if(this.store){
9696             this.store.afterReject(this);
9697         }
9698     },
9699
9700     /**
9701      * Usually called by the {@link Roo.data.Store} which owns the Record.
9702      * Commits all changes made to the Record since either creation, or the last commit operation.
9703      * <p>
9704      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9705      * of commit operations.
9706      */
9707     commit : function(){
9708         this.dirty = false;
9709         delete this.modified;
9710         this.editing = false;
9711         if(this.store){
9712             this.store.afterCommit(this);
9713         }
9714     },
9715
9716     // private
9717     hasError : function(){
9718         return this.error != null;
9719     },
9720
9721     // private
9722     clearError : function(){
9723         this.error = null;
9724     },
9725
9726     /**
9727      * Creates a copy of this record.
9728      * @param {String} id (optional) A new record id if you don't want to use this record's id
9729      * @return {Record}
9730      */
9731     copy : function(newId) {
9732         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9733     }
9734 };/*
9735  * Based on:
9736  * Ext JS Library 1.1.1
9737  * Copyright(c) 2006-2007, Ext JS, LLC.
9738  *
9739  * Originally Released Under LGPL - original licence link has changed is not relivant.
9740  *
9741  * Fork - LGPL
9742  * <script type="text/javascript">
9743  */
9744
9745
9746
9747 /**
9748  * @class Roo.data.Store
9749  * @extends Roo.util.Observable
9750  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9751  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9752  * <p>
9753  * 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
9754  * has no knowledge of the format of the data returned by the Proxy.<br>
9755  * <p>
9756  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9757  * instances from the data object. These records are cached and made available through accessor functions.
9758  * @constructor
9759  * Creates a new Store.
9760  * @param {Object} config A config object containing the objects needed for the Store to access data,
9761  * and read the data into Records.
9762  */
9763 Roo.data.Store = function(config){
9764     this.data = new Roo.util.MixedCollection(false);
9765     this.data.getKey = function(o){
9766         return o.id;
9767     };
9768     this.baseParams = {};
9769     // private
9770     this.paramNames = {
9771         "start" : "start",
9772         "limit" : "limit",
9773         "sort" : "sort",
9774         "dir" : "dir",
9775         "multisort" : "_multisort"
9776     };
9777
9778     if(config && config.data){
9779         this.inlineData = config.data;
9780         delete config.data;
9781     }
9782
9783     Roo.apply(this, config);
9784     
9785     if(this.reader){ // reader passed
9786         this.reader = Roo.factory(this.reader, Roo.data);
9787         this.reader.xmodule = this.xmodule || false;
9788         if(!this.recordType){
9789             this.recordType = this.reader.recordType;
9790         }
9791         if(this.reader.onMetaChange){
9792             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9793         }
9794     }
9795
9796     if(this.recordType){
9797         this.fields = this.recordType.prototype.fields;
9798     }
9799     this.modified = [];
9800
9801     this.addEvents({
9802         /**
9803          * @event datachanged
9804          * Fires when the data cache has changed, and a widget which is using this Store
9805          * as a Record cache should refresh its view.
9806          * @param {Store} this
9807          */
9808         datachanged : true,
9809         /**
9810          * @event metachange
9811          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9812          * @param {Store} this
9813          * @param {Object} meta The JSON metadata
9814          */
9815         metachange : true,
9816         /**
9817          * @event add
9818          * Fires when Records have been added to the Store
9819          * @param {Store} this
9820          * @param {Roo.data.Record[]} records The array of Records added
9821          * @param {Number} index The index at which the record(s) were added
9822          */
9823         add : true,
9824         /**
9825          * @event remove
9826          * Fires when a Record has been removed from the Store
9827          * @param {Store} this
9828          * @param {Roo.data.Record} record The Record that was removed
9829          * @param {Number} index The index at which the record was removed
9830          */
9831         remove : true,
9832         /**
9833          * @event update
9834          * Fires when a Record has been updated
9835          * @param {Store} this
9836          * @param {Roo.data.Record} record The Record that was updated
9837          * @param {String} operation The update operation being performed.  Value may be one of:
9838          * <pre><code>
9839  Roo.data.Record.EDIT
9840  Roo.data.Record.REJECT
9841  Roo.data.Record.COMMIT
9842          * </code></pre>
9843          */
9844         update : true,
9845         /**
9846          * @event clear
9847          * Fires when the data cache has been cleared.
9848          * @param {Store} this
9849          */
9850         clear : true,
9851         /**
9852          * @event beforeload
9853          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9854          * the load action will be canceled.
9855          * @param {Store} this
9856          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9857          */
9858         beforeload : true,
9859         /**
9860          * @event beforeloadadd
9861          * Fires after a new set of Records has been loaded.
9862          * @param {Store} this
9863          * @param {Roo.data.Record[]} records The Records that were loaded
9864          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9865          */
9866         beforeloadadd : true,
9867         /**
9868          * @event load
9869          * Fires after a new set of Records has been loaded, before they are added to the store.
9870          * @param {Store} this
9871          * @param {Roo.data.Record[]} records The Records that were loaded
9872          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9873          * @params {Object} return from reader
9874          */
9875         load : true,
9876         /**
9877          * @event loadexception
9878          * Fires if an exception occurs in the Proxy during loading.
9879          * Called with the signature of the Proxy's "loadexception" event.
9880          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9881          * 
9882          * @param {Proxy} 
9883          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9884          * @param {Object} load options 
9885          * @param {Object} jsonData from your request (normally this contains the Exception)
9886          */
9887         loadexception : true
9888     });
9889     
9890     if(this.proxy){
9891         this.proxy = Roo.factory(this.proxy, Roo.data);
9892         this.proxy.xmodule = this.xmodule || false;
9893         this.relayEvents(this.proxy,  ["loadexception"]);
9894     }
9895     this.sortToggle = {};
9896     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9897
9898     Roo.data.Store.superclass.constructor.call(this);
9899
9900     if(this.inlineData){
9901         this.loadData(this.inlineData);
9902         delete this.inlineData;
9903     }
9904 };
9905
9906 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9907      /**
9908     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9909     * without a remote query - used by combo/forms at present.
9910     */
9911     
9912     /**
9913     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9914     */
9915     /**
9916     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9917     */
9918     /**
9919     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9920     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9921     */
9922     /**
9923     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9924     * on any HTTP request
9925     */
9926     /**
9927     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9928     */
9929     /**
9930     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9931     */
9932     multiSort: false,
9933     /**
9934     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9935     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9936     */
9937     remoteSort : false,
9938
9939     /**
9940     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9941      * loaded or when a record is removed. (defaults to false).
9942     */
9943     pruneModifiedRecords : false,
9944
9945     // private
9946     lastOptions : null,
9947
9948     /**
9949      * Add Records to the Store and fires the add event.
9950      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9951      */
9952     add : function(records){
9953         records = [].concat(records);
9954         for(var i = 0, len = records.length; i < len; i++){
9955             records[i].join(this);
9956         }
9957         var index = this.data.length;
9958         this.data.addAll(records);
9959         this.fireEvent("add", this, records, index);
9960     },
9961
9962     /**
9963      * Remove a Record from the Store and fires the remove event.
9964      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9965      */
9966     remove : function(record){
9967         var index = this.data.indexOf(record);
9968         this.data.removeAt(index);
9969         if(this.pruneModifiedRecords){
9970             this.modified.remove(record);
9971         }
9972         this.fireEvent("remove", this, record, index);
9973     },
9974
9975     /**
9976      * Remove all Records from the Store and fires the clear event.
9977      */
9978     removeAll : function(){
9979         this.data.clear();
9980         if(this.pruneModifiedRecords){
9981             this.modified = [];
9982         }
9983         this.fireEvent("clear", this);
9984     },
9985
9986     /**
9987      * Inserts Records to the Store at the given index and fires the add event.
9988      * @param {Number} index The start index at which to insert the passed Records.
9989      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9990      */
9991     insert : function(index, records){
9992         records = [].concat(records);
9993         for(var i = 0, len = records.length; i < len; i++){
9994             this.data.insert(index, records[i]);
9995             records[i].join(this);
9996         }
9997         this.fireEvent("add", this, records, index);
9998     },
9999
10000     /**
10001      * Get the index within the cache of the passed Record.
10002      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10003      * @return {Number} The index of the passed Record. Returns -1 if not found.
10004      */
10005     indexOf : function(record){
10006         return this.data.indexOf(record);
10007     },
10008
10009     /**
10010      * Get the index within the cache of the Record with the passed id.
10011      * @param {String} id The id of the Record to find.
10012      * @return {Number} The index of the Record. Returns -1 if not found.
10013      */
10014     indexOfId : function(id){
10015         return this.data.indexOfKey(id);
10016     },
10017
10018     /**
10019      * Get the Record with the specified id.
10020      * @param {String} id The id of the Record to find.
10021      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10022      */
10023     getById : function(id){
10024         return this.data.key(id);
10025     },
10026
10027     /**
10028      * Get the Record at the specified index.
10029      * @param {Number} index The index of the Record to find.
10030      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10031      */
10032     getAt : function(index){
10033         return this.data.itemAt(index);
10034     },
10035
10036     /**
10037      * Returns a range of Records between specified indices.
10038      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10039      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10040      * @return {Roo.data.Record[]} An array of Records
10041      */
10042     getRange : function(start, end){
10043         return this.data.getRange(start, end);
10044     },
10045
10046     // private
10047     storeOptions : function(o){
10048         o = Roo.apply({}, o);
10049         delete o.callback;
10050         delete o.scope;
10051         this.lastOptions = o;
10052     },
10053
10054     /**
10055      * Loads the Record cache from the configured Proxy using the configured Reader.
10056      * <p>
10057      * If using remote paging, then the first load call must specify the <em>start</em>
10058      * and <em>limit</em> properties in the options.params property to establish the initial
10059      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10060      * <p>
10061      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10062      * and this call will return before the new data has been loaded. Perform any post-processing
10063      * in a callback function, or in a "load" event handler.</strong>
10064      * <p>
10065      * @param {Object} options An object containing properties which control loading options:<ul>
10066      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10067      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10068      * passed the following arguments:<ul>
10069      * <li>r : Roo.data.Record[]</li>
10070      * <li>options: Options object from the load call</li>
10071      * <li>success: Boolean success indicator</li></ul></li>
10072      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10073      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10074      * </ul>
10075      */
10076     load : function(options){
10077         options = options || {};
10078         if(this.fireEvent("beforeload", this, options) !== false){
10079             this.storeOptions(options);
10080             var p = Roo.apply(options.params || {}, this.baseParams);
10081             // if meta was not loaded from remote source.. try requesting it.
10082             if (!this.reader.metaFromRemote) {
10083                 p._requestMeta = 1;
10084             }
10085             if(this.sortInfo && this.remoteSort){
10086                 var pn = this.paramNames;
10087                 p[pn["sort"]] = this.sortInfo.field;
10088                 p[pn["dir"]] = this.sortInfo.direction;
10089             }
10090             if (this.multiSort) {
10091                 var pn = this.paramNames;
10092                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10093             }
10094             
10095             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10096         }
10097     },
10098
10099     /**
10100      * Reloads the Record cache from the configured Proxy using the configured Reader and
10101      * the options from the last load operation performed.
10102      * @param {Object} options (optional) An object containing properties which may override the options
10103      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10104      * the most recently used options are reused).
10105      */
10106     reload : function(options){
10107         this.load(Roo.applyIf(options||{}, this.lastOptions));
10108     },
10109
10110     // private
10111     // Called as a callback by the Reader during a load operation.
10112     loadRecords : function(o, options, success){
10113         if(!o || success === false){
10114             if(success !== false){
10115                 this.fireEvent("load", this, [], options, o);
10116             }
10117             if(options.callback){
10118                 options.callback.call(options.scope || this, [], options, false);
10119             }
10120             return;
10121         }
10122         // if data returned failure - throw an exception.
10123         if (o.success === false) {
10124             // show a message if no listener is registered.
10125             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10126                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10127             }
10128             // loadmask wil be hooked into this..
10129             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10130             return;
10131         }
10132         var r = o.records, t = o.totalRecords || r.length;
10133         
10134         this.fireEvent("beforeloadadd", this, r, options, o);
10135         
10136         if(!options || options.add !== true){
10137             if(this.pruneModifiedRecords){
10138                 this.modified = [];
10139             }
10140             for(var i = 0, len = r.length; i < len; i++){
10141                 r[i].join(this);
10142             }
10143             if(this.snapshot){
10144                 this.data = this.snapshot;
10145                 delete this.snapshot;
10146             }
10147             this.data.clear();
10148             this.data.addAll(r);
10149             this.totalLength = t;
10150             this.applySort();
10151             this.fireEvent("datachanged", this);
10152         }else{
10153             this.totalLength = Math.max(t, this.data.length+r.length);
10154             this.add(r);
10155         }
10156         this.fireEvent("load", this, r, options, o);
10157         if(options.callback){
10158             options.callback.call(options.scope || this, r, options, true);
10159         }
10160     },
10161
10162
10163     /**
10164      * Loads data from a passed data block. A Reader which understands the format of the data
10165      * must have been configured in the constructor.
10166      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10167      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10168      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10169      */
10170     loadData : function(o, append){
10171         var r = this.reader.readRecords(o);
10172         this.loadRecords(r, {add: append}, true);
10173     },
10174
10175     /**
10176      * Gets the number of cached records.
10177      * <p>
10178      * <em>If using paging, this may not be the total size of the dataset. If the data object
10179      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10180      * the data set size</em>
10181      */
10182     getCount : function(){
10183         return this.data.length || 0;
10184     },
10185
10186     /**
10187      * Gets the total number of records in the dataset as returned by the server.
10188      * <p>
10189      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10190      * the dataset size</em>
10191      */
10192     getTotalCount : function(){
10193         return this.totalLength || 0;
10194     },
10195
10196     /**
10197      * Returns the sort state of the Store as an object with two properties:
10198      * <pre><code>
10199  field {String} The name of the field by which the Records are sorted
10200  direction {String} The sort order, "ASC" or "DESC"
10201      * </code></pre>
10202      */
10203     getSortState : function(){
10204         return this.sortInfo;
10205     },
10206
10207     // private
10208     applySort : function(){
10209         if(this.sortInfo && !this.remoteSort){
10210             var s = this.sortInfo, f = s.field;
10211             var st = this.fields.get(f).sortType;
10212             var fn = function(r1, r2){
10213                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10214                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10215             };
10216             this.data.sort(s.direction, fn);
10217             if(this.snapshot && this.snapshot != this.data){
10218                 this.snapshot.sort(s.direction, fn);
10219             }
10220         }
10221     },
10222
10223     /**
10224      * Sets the default sort column and order to be used by the next load operation.
10225      * @param {String} fieldName The name of the field to sort by.
10226      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10227      */
10228     setDefaultSort : function(field, dir){
10229         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10230     },
10231
10232     /**
10233      * Sort the Records.
10234      * If remote sorting is used, the sort is performed on the server, and the cache is
10235      * reloaded. If local sorting is used, the cache is sorted internally.
10236      * @param {String} fieldName The name of the field to sort by.
10237      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10238      */
10239     sort : function(fieldName, dir){
10240         var f = this.fields.get(fieldName);
10241         if(!dir){
10242             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10243             
10244             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10245                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10246             }else{
10247                 dir = f.sortDir;
10248             }
10249         }
10250         this.sortToggle[f.name] = dir;
10251         this.sortInfo = {field: f.name, direction: dir};
10252         if(!this.remoteSort){
10253             this.applySort();
10254             this.fireEvent("datachanged", this);
10255         }else{
10256             this.load(this.lastOptions);
10257         }
10258     },
10259
10260     /**
10261      * Calls the specified function for each of the Records in the cache.
10262      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10263      * Returning <em>false</em> aborts and exits the iteration.
10264      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10265      */
10266     each : function(fn, scope){
10267         this.data.each(fn, scope);
10268     },
10269
10270     /**
10271      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10272      * (e.g., during paging).
10273      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10274      */
10275     getModifiedRecords : function(){
10276         return this.modified;
10277     },
10278
10279     // private
10280     createFilterFn : function(property, value, anyMatch){
10281         if(!value.exec){ // not a regex
10282             value = String(value);
10283             if(value.length == 0){
10284                 return false;
10285             }
10286             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10287         }
10288         return function(r){
10289             return value.test(r.data[property]);
10290         };
10291     },
10292
10293     /**
10294      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10295      * @param {String} property A field on your records
10296      * @param {Number} start The record index to start at (defaults to 0)
10297      * @param {Number} end The last record index to include (defaults to length - 1)
10298      * @return {Number} The sum
10299      */
10300     sum : function(property, start, end){
10301         var rs = this.data.items, v = 0;
10302         start = start || 0;
10303         end = (end || end === 0) ? end : rs.length-1;
10304
10305         for(var i = start; i <= end; i++){
10306             v += (rs[i].data[property] || 0);
10307         }
10308         return v;
10309     },
10310
10311     /**
10312      * Filter the records by a specified property.
10313      * @param {String} field A field on your records
10314      * @param {String/RegExp} value Either a string that the field
10315      * should start with or a RegExp to test against the field
10316      * @param {Boolean} anyMatch True to match any part not just the beginning
10317      */
10318     filter : function(property, value, anyMatch){
10319         var fn = this.createFilterFn(property, value, anyMatch);
10320         return fn ? this.filterBy(fn) : this.clearFilter();
10321     },
10322
10323     /**
10324      * Filter by a function. The specified function will be called with each
10325      * record in this data source. If the function returns true the record is included,
10326      * otherwise it is filtered.
10327      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10328      * @param {Object} scope (optional) The scope of the function (defaults to this)
10329      */
10330     filterBy : function(fn, scope){
10331         this.snapshot = this.snapshot || this.data;
10332         this.data = this.queryBy(fn, scope||this);
10333         this.fireEvent("datachanged", this);
10334     },
10335
10336     /**
10337      * Query the records by a specified property.
10338      * @param {String} field A field on your records
10339      * @param {String/RegExp} value Either a string that the field
10340      * should start with or a RegExp to test against the field
10341      * @param {Boolean} anyMatch True to match any part not just the beginning
10342      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10343      */
10344     query : function(property, value, anyMatch){
10345         var fn = this.createFilterFn(property, value, anyMatch);
10346         return fn ? this.queryBy(fn) : this.data.clone();
10347     },
10348
10349     /**
10350      * Query by a function. The specified function will be called with each
10351      * record in this data source. If the function returns true the record is included
10352      * in the results.
10353      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10354      * @param {Object} scope (optional) The scope of the function (defaults to this)
10355       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10356      **/
10357     queryBy : function(fn, scope){
10358         var data = this.snapshot || this.data;
10359         return data.filterBy(fn, scope||this);
10360     },
10361
10362     /**
10363      * Collects unique values for a particular dataIndex from this store.
10364      * @param {String} dataIndex The property to collect
10365      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10366      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10367      * @return {Array} An array of the unique values
10368      **/
10369     collect : function(dataIndex, allowNull, bypassFilter){
10370         var d = (bypassFilter === true && this.snapshot) ?
10371                 this.snapshot.items : this.data.items;
10372         var v, sv, r = [], l = {};
10373         for(var i = 0, len = d.length; i < len; i++){
10374             v = d[i].data[dataIndex];
10375             sv = String(v);
10376             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10377                 l[sv] = true;
10378                 r[r.length] = v;
10379             }
10380         }
10381         return r;
10382     },
10383
10384     /**
10385      * Revert to a view of the Record cache with no filtering applied.
10386      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10387      */
10388     clearFilter : function(suppressEvent){
10389         if(this.snapshot && this.snapshot != this.data){
10390             this.data = this.snapshot;
10391             delete this.snapshot;
10392             if(suppressEvent !== true){
10393                 this.fireEvent("datachanged", this);
10394             }
10395         }
10396     },
10397
10398     // private
10399     afterEdit : function(record){
10400         if(this.modified.indexOf(record) == -1){
10401             this.modified.push(record);
10402         }
10403         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10404     },
10405     
10406     // private
10407     afterReject : function(record){
10408         this.modified.remove(record);
10409         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10410     },
10411
10412     // private
10413     afterCommit : function(record){
10414         this.modified.remove(record);
10415         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10416     },
10417
10418     /**
10419      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10420      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10421      */
10422     commitChanges : function(){
10423         var m = this.modified.slice(0);
10424         this.modified = [];
10425         for(var i = 0, len = m.length; i < len; i++){
10426             m[i].commit();
10427         }
10428     },
10429
10430     /**
10431      * Cancel outstanding changes on all changed records.
10432      */
10433     rejectChanges : function(){
10434         var m = this.modified.slice(0);
10435         this.modified = [];
10436         for(var i = 0, len = m.length; i < len; i++){
10437             m[i].reject();
10438         }
10439     },
10440
10441     onMetaChange : function(meta, rtype, o){
10442         this.recordType = rtype;
10443         this.fields = rtype.prototype.fields;
10444         delete this.snapshot;
10445         this.sortInfo = meta.sortInfo || this.sortInfo;
10446         this.modified = [];
10447         this.fireEvent('metachange', this, this.reader.meta);
10448     },
10449     
10450     moveIndex : function(data, type)
10451     {
10452         var index = this.indexOf(data);
10453         
10454         var newIndex = index + type;
10455         
10456         this.remove(data);
10457         
10458         this.insert(newIndex, data);
10459         
10460     }
10461 });/*
10462  * Based on:
10463  * Ext JS Library 1.1.1
10464  * Copyright(c) 2006-2007, Ext JS, LLC.
10465  *
10466  * Originally Released Under LGPL - original licence link has changed is not relivant.
10467  *
10468  * Fork - LGPL
10469  * <script type="text/javascript">
10470  */
10471
10472 /**
10473  * @class Roo.data.SimpleStore
10474  * @extends Roo.data.Store
10475  * Small helper class to make creating Stores from Array data easier.
10476  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10477  * @cfg {Array} fields An array of field definition objects, or field name strings.
10478  * @cfg {Array} data The multi-dimensional array of data
10479  * @constructor
10480  * @param {Object} config
10481  */
10482 Roo.data.SimpleStore = function(config){
10483     Roo.data.SimpleStore.superclass.constructor.call(this, {
10484         isLocal : true,
10485         reader: new Roo.data.ArrayReader({
10486                 id: config.id
10487             },
10488             Roo.data.Record.create(config.fields)
10489         ),
10490         proxy : new Roo.data.MemoryProxy(config.data)
10491     });
10492     this.load();
10493 };
10494 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10495  * Based on:
10496  * Ext JS Library 1.1.1
10497  * Copyright(c) 2006-2007, Ext JS, LLC.
10498  *
10499  * Originally Released Under LGPL - original licence link has changed is not relivant.
10500  *
10501  * Fork - LGPL
10502  * <script type="text/javascript">
10503  */
10504
10505 /**
10506 /**
10507  * @extends Roo.data.Store
10508  * @class Roo.data.JsonStore
10509  * Small helper class to make creating Stores for JSON data easier. <br/>
10510 <pre><code>
10511 var store = new Roo.data.JsonStore({
10512     url: 'get-images.php',
10513     root: 'images',
10514     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10515 });
10516 </code></pre>
10517  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10518  * JsonReader and HttpProxy (unless inline data is provided).</b>
10519  * @cfg {Array} fields An array of field definition objects, or field name strings.
10520  * @constructor
10521  * @param {Object} config
10522  */
10523 Roo.data.JsonStore = function(c){
10524     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10525         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10526         reader: new Roo.data.JsonReader(c, c.fields)
10527     }));
10528 };
10529 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10530  * Based on:
10531  * Ext JS Library 1.1.1
10532  * Copyright(c) 2006-2007, Ext JS, LLC.
10533  *
10534  * Originally Released Under LGPL - original licence link has changed is not relivant.
10535  *
10536  * Fork - LGPL
10537  * <script type="text/javascript">
10538  */
10539
10540  
10541 Roo.data.Field = function(config){
10542     if(typeof config == "string"){
10543         config = {name: config};
10544     }
10545     Roo.apply(this, config);
10546     
10547     if(!this.type){
10548         this.type = "auto";
10549     }
10550     
10551     var st = Roo.data.SortTypes;
10552     // named sortTypes are supported, here we look them up
10553     if(typeof this.sortType == "string"){
10554         this.sortType = st[this.sortType];
10555     }
10556     
10557     // set default sortType for strings and dates
10558     if(!this.sortType){
10559         switch(this.type){
10560             case "string":
10561                 this.sortType = st.asUCString;
10562                 break;
10563             case "date":
10564                 this.sortType = st.asDate;
10565                 break;
10566             default:
10567                 this.sortType = st.none;
10568         }
10569     }
10570
10571     // define once
10572     var stripRe = /[\$,%]/g;
10573
10574     // prebuilt conversion function for this field, instead of
10575     // switching every time we're reading a value
10576     if(!this.convert){
10577         var cv, dateFormat = this.dateFormat;
10578         switch(this.type){
10579             case "":
10580             case "auto":
10581             case undefined:
10582                 cv = function(v){ return v; };
10583                 break;
10584             case "string":
10585                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10586                 break;
10587             case "int":
10588                 cv = function(v){
10589                     return v !== undefined && v !== null && v !== '' ?
10590                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10591                     };
10592                 break;
10593             case "float":
10594                 cv = function(v){
10595                     return v !== undefined && v !== null && v !== '' ?
10596                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10597                     };
10598                 break;
10599             case "bool":
10600             case "boolean":
10601                 cv = function(v){ return v === true || v === "true" || v == 1; };
10602                 break;
10603             case "date":
10604                 cv = function(v){
10605                     if(!v){
10606                         return '';
10607                     }
10608                     if(v instanceof Date){
10609                         return v;
10610                     }
10611                     if(dateFormat){
10612                         if(dateFormat == "timestamp"){
10613                             return new Date(v*1000);
10614                         }
10615                         return Date.parseDate(v, dateFormat);
10616                     }
10617                     var parsed = Date.parse(v);
10618                     return parsed ? new Date(parsed) : null;
10619                 };
10620              break;
10621             
10622         }
10623         this.convert = cv;
10624     }
10625 };
10626
10627 Roo.data.Field.prototype = {
10628     dateFormat: null,
10629     defaultValue: "",
10630     mapping: null,
10631     sortType : null,
10632     sortDir : "ASC"
10633 };/*
10634  * Based on:
10635  * Ext JS Library 1.1.1
10636  * Copyright(c) 2006-2007, Ext JS, LLC.
10637  *
10638  * Originally Released Under LGPL - original licence link has changed is not relivant.
10639  *
10640  * Fork - LGPL
10641  * <script type="text/javascript">
10642  */
10643  
10644 // Base class for reading structured data from a data source.  This class is intended to be
10645 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10646
10647 /**
10648  * @class Roo.data.DataReader
10649  * Base class for reading structured data from a data source.  This class is intended to be
10650  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10651  */
10652
10653 Roo.data.DataReader = function(meta, recordType){
10654     
10655     this.meta = meta;
10656     
10657     this.recordType = recordType instanceof Array ? 
10658         Roo.data.Record.create(recordType) : recordType;
10659 };
10660
10661 Roo.data.DataReader.prototype = {
10662      /**
10663      * Create an empty record
10664      * @param {Object} data (optional) - overlay some values
10665      * @return {Roo.data.Record} record created.
10666      */
10667     newRow :  function(d) {
10668         var da =  {};
10669         this.recordType.prototype.fields.each(function(c) {
10670             switch( c.type) {
10671                 case 'int' : da[c.name] = 0; break;
10672                 case 'date' : da[c.name] = new Date(); break;
10673                 case 'float' : da[c.name] = 0.0; break;
10674                 case 'boolean' : da[c.name] = false; break;
10675                 default : da[c.name] = ""; break;
10676             }
10677             
10678         });
10679         return new this.recordType(Roo.apply(da, d));
10680     }
10681     
10682 };/*
10683  * Based on:
10684  * Ext JS Library 1.1.1
10685  * Copyright(c) 2006-2007, Ext JS, LLC.
10686  *
10687  * Originally Released Under LGPL - original licence link has changed is not relivant.
10688  *
10689  * Fork - LGPL
10690  * <script type="text/javascript">
10691  */
10692
10693 /**
10694  * @class Roo.data.DataProxy
10695  * @extends Roo.data.Observable
10696  * This class is an abstract base class for implementations which provide retrieval of
10697  * unformatted data objects.<br>
10698  * <p>
10699  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10700  * (of the appropriate type which knows how to parse the data object) to provide a block of
10701  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10702  * <p>
10703  * Custom implementations must implement the load method as described in
10704  * {@link Roo.data.HttpProxy#load}.
10705  */
10706 Roo.data.DataProxy = function(){
10707     this.addEvents({
10708         /**
10709          * @event beforeload
10710          * Fires before a network request is made to retrieve a data object.
10711          * @param {Object} This DataProxy object.
10712          * @param {Object} params The params parameter to the load function.
10713          */
10714         beforeload : true,
10715         /**
10716          * @event load
10717          * Fires before the load method's callback is called.
10718          * @param {Object} This DataProxy object.
10719          * @param {Object} o The data object.
10720          * @param {Object} arg The callback argument object passed to the load function.
10721          */
10722         load : true,
10723         /**
10724          * @event loadexception
10725          * Fires if an Exception occurs during data retrieval.
10726          * @param {Object} This DataProxy object.
10727          * @param {Object} o The data object.
10728          * @param {Object} arg The callback argument object passed to the load function.
10729          * @param {Object} e The Exception.
10730          */
10731         loadexception : true
10732     });
10733     Roo.data.DataProxy.superclass.constructor.call(this);
10734 };
10735
10736 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10737
10738     /**
10739      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10740      */
10741 /*
10742  * Based on:
10743  * Ext JS Library 1.1.1
10744  * Copyright(c) 2006-2007, Ext JS, LLC.
10745  *
10746  * Originally Released Under LGPL - original licence link has changed is not relivant.
10747  *
10748  * Fork - LGPL
10749  * <script type="text/javascript">
10750  */
10751 /**
10752  * @class Roo.data.MemoryProxy
10753  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10754  * to the Reader when its load method is called.
10755  * @constructor
10756  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10757  */
10758 Roo.data.MemoryProxy = function(data){
10759     if (data.data) {
10760         data = data.data;
10761     }
10762     Roo.data.MemoryProxy.superclass.constructor.call(this);
10763     this.data = data;
10764 };
10765
10766 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10767     /**
10768      * Load data from the requested source (in this case an in-memory
10769      * data object passed to the constructor), read the data object into
10770      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10771      * process that block using the passed callback.
10772      * @param {Object} params This parameter is not used by the MemoryProxy class.
10773      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10774      * object into a block of Roo.data.Records.
10775      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10776      * The function must be passed <ul>
10777      * <li>The Record block object</li>
10778      * <li>The "arg" argument from the load function</li>
10779      * <li>A boolean success indicator</li>
10780      * </ul>
10781      * @param {Object} scope The scope in which to call the callback
10782      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10783      */
10784     load : function(params, reader, callback, scope, arg){
10785         params = params || {};
10786         var result;
10787         try {
10788             result = reader.readRecords(this.data);
10789         }catch(e){
10790             this.fireEvent("loadexception", this, arg, null, e);
10791             callback.call(scope, null, arg, false);
10792             return;
10793         }
10794         callback.call(scope, result, arg, true);
10795     },
10796     
10797     // private
10798     update : function(params, records){
10799         
10800     }
10801 });/*
10802  * Based on:
10803  * Ext JS Library 1.1.1
10804  * Copyright(c) 2006-2007, Ext JS, LLC.
10805  *
10806  * Originally Released Under LGPL - original licence link has changed is not relivant.
10807  *
10808  * Fork - LGPL
10809  * <script type="text/javascript">
10810  */
10811 /**
10812  * @class Roo.data.HttpProxy
10813  * @extends Roo.data.DataProxy
10814  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10815  * configured to reference a certain URL.<br><br>
10816  * <p>
10817  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10818  * from which the running page was served.<br><br>
10819  * <p>
10820  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10821  * <p>
10822  * Be aware that to enable the browser to parse an XML document, the server must set
10823  * the Content-Type header in the HTTP response to "text/xml".
10824  * @constructor
10825  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10826  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10827  * will be used to make the request.
10828  */
10829 Roo.data.HttpProxy = function(conn){
10830     Roo.data.HttpProxy.superclass.constructor.call(this);
10831     // is conn a conn config or a real conn?
10832     this.conn = conn;
10833     this.useAjax = !conn || !conn.events;
10834   
10835 };
10836
10837 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10838     // thse are take from connection...
10839     
10840     /**
10841      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10842      */
10843     /**
10844      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10845      * extra parameters to each request made by this object. (defaults to undefined)
10846      */
10847     /**
10848      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10849      *  to each request made by this object. (defaults to undefined)
10850      */
10851     /**
10852      * @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)
10853      */
10854     /**
10855      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10856      */
10857      /**
10858      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10859      * @type Boolean
10860      */
10861   
10862
10863     /**
10864      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10865      * @type Boolean
10866      */
10867     /**
10868      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10869      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10870      * a finer-grained basis than the DataProxy events.
10871      */
10872     getConnection : function(){
10873         return this.useAjax ? Roo.Ajax : this.conn;
10874     },
10875
10876     /**
10877      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10878      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10879      * process that block using the passed callback.
10880      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10881      * for the request to the remote server.
10882      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10883      * object into a block of Roo.data.Records.
10884      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10885      * The function must be passed <ul>
10886      * <li>The Record block object</li>
10887      * <li>The "arg" argument from the load function</li>
10888      * <li>A boolean success indicator</li>
10889      * </ul>
10890      * @param {Object} scope The scope in which to call the callback
10891      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10892      */
10893     load : function(params, reader, callback, scope, arg){
10894         if(this.fireEvent("beforeload", this, params) !== false){
10895             var  o = {
10896                 params : params || {},
10897                 request: {
10898                     callback : callback,
10899                     scope : scope,
10900                     arg : arg
10901                 },
10902                 reader: reader,
10903                 callback : this.loadResponse,
10904                 scope: this
10905             };
10906             if(this.useAjax){
10907                 Roo.applyIf(o, this.conn);
10908                 if(this.activeRequest){
10909                     Roo.Ajax.abort(this.activeRequest);
10910                 }
10911                 this.activeRequest = Roo.Ajax.request(o);
10912             }else{
10913                 this.conn.request(o);
10914             }
10915         }else{
10916             callback.call(scope||this, null, arg, false);
10917         }
10918     },
10919
10920     // private
10921     loadResponse : function(o, success, response){
10922         delete this.activeRequest;
10923         if(!success){
10924             this.fireEvent("loadexception", this, o, response);
10925             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10926             return;
10927         }
10928         var result;
10929         try {
10930             result = o.reader.read(response);
10931         }catch(e){
10932             this.fireEvent("loadexception", this, o, response, e);
10933             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10934             return;
10935         }
10936         
10937         this.fireEvent("load", this, o, o.request.arg);
10938         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10939     },
10940
10941     // private
10942     update : function(dataSet){
10943
10944     },
10945
10946     // private
10947     updateResponse : function(dataSet){
10948
10949     }
10950 });/*
10951  * Based on:
10952  * Ext JS Library 1.1.1
10953  * Copyright(c) 2006-2007, Ext JS, LLC.
10954  *
10955  * Originally Released Under LGPL - original licence link has changed is not relivant.
10956  *
10957  * Fork - LGPL
10958  * <script type="text/javascript">
10959  */
10960
10961 /**
10962  * @class Roo.data.ScriptTagProxy
10963  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10964  * other than the originating domain of the running page.<br><br>
10965  * <p>
10966  * <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
10967  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10968  * <p>
10969  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10970  * source code that is used as the source inside a &lt;script> tag.<br><br>
10971  * <p>
10972  * In order for the browser to process the returned data, the server must wrap the data object
10973  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10974  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10975  * depending on whether the callback name was passed:
10976  * <p>
10977  * <pre><code>
10978 boolean scriptTag = false;
10979 String cb = request.getParameter("callback");
10980 if (cb != null) {
10981     scriptTag = true;
10982     response.setContentType("text/javascript");
10983 } else {
10984     response.setContentType("application/x-json");
10985 }
10986 Writer out = response.getWriter();
10987 if (scriptTag) {
10988     out.write(cb + "(");
10989 }
10990 out.print(dataBlock.toJsonString());
10991 if (scriptTag) {
10992     out.write(");");
10993 }
10994 </pre></code>
10995  *
10996  * @constructor
10997  * @param {Object} config A configuration object.
10998  */
10999 Roo.data.ScriptTagProxy = function(config){
11000     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11001     Roo.apply(this, config);
11002     this.head = document.getElementsByTagName("head")[0];
11003 };
11004
11005 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11006
11007 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11008     /**
11009      * @cfg {String} url The URL from which to request the data object.
11010      */
11011     /**
11012      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11013      */
11014     timeout : 30000,
11015     /**
11016      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11017      * the server the name of the callback function set up by the load call to process the returned data object.
11018      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11019      * javascript output which calls this named function passing the data object as its only parameter.
11020      */
11021     callbackParam : "callback",
11022     /**
11023      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11024      * name to the request.
11025      */
11026     nocache : true,
11027
11028     /**
11029      * Load data from the configured URL, read the data object into
11030      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11031      * process that block using the passed callback.
11032      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11033      * for the request to the remote server.
11034      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11035      * object into a block of Roo.data.Records.
11036      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11037      * The function must be passed <ul>
11038      * <li>The Record block object</li>
11039      * <li>The "arg" argument from the load function</li>
11040      * <li>A boolean success indicator</li>
11041      * </ul>
11042      * @param {Object} scope The scope in which to call the callback
11043      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11044      */
11045     load : function(params, reader, callback, scope, arg){
11046         if(this.fireEvent("beforeload", this, params) !== false){
11047
11048             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11049
11050             var url = this.url;
11051             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11052             if(this.nocache){
11053                 url += "&_dc=" + (new Date().getTime());
11054             }
11055             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11056             var trans = {
11057                 id : transId,
11058                 cb : "stcCallback"+transId,
11059                 scriptId : "stcScript"+transId,
11060                 params : params,
11061                 arg : arg,
11062                 url : url,
11063                 callback : callback,
11064                 scope : scope,
11065                 reader : reader
11066             };
11067             var conn = this;
11068
11069             window[trans.cb] = function(o){
11070                 conn.handleResponse(o, trans);
11071             };
11072
11073             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11074
11075             if(this.autoAbort !== false){
11076                 this.abort();
11077             }
11078
11079             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11080
11081             var script = document.createElement("script");
11082             script.setAttribute("src", url);
11083             script.setAttribute("type", "text/javascript");
11084             script.setAttribute("id", trans.scriptId);
11085             this.head.appendChild(script);
11086
11087             this.trans = trans;
11088         }else{
11089             callback.call(scope||this, null, arg, false);
11090         }
11091     },
11092
11093     // private
11094     isLoading : function(){
11095         return this.trans ? true : false;
11096     },
11097
11098     /**
11099      * Abort the current server request.
11100      */
11101     abort : function(){
11102         if(this.isLoading()){
11103             this.destroyTrans(this.trans);
11104         }
11105     },
11106
11107     // private
11108     destroyTrans : function(trans, isLoaded){
11109         this.head.removeChild(document.getElementById(trans.scriptId));
11110         clearTimeout(trans.timeoutId);
11111         if(isLoaded){
11112             window[trans.cb] = undefined;
11113             try{
11114                 delete window[trans.cb];
11115             }catch(e){}
11116         }else{
11117             // if hasn't been loaded, wait for load to remove it to prevent script error
11118             window[trans.cb] = function(){
11119                 window[trans.cb] = undefined;
11120                 try{
11121                     delete window[trans.cb];
11122                 }catch(e){}
11123             };
11124         }
11125     },
11126
11127     // private
11128     handleResponse : function(o, trans){
11129         this.trans = false;
11130         this.destroyTrans(trans, true);
11131         var result;
11132         try {
11133             result = trans.reader.readRecords(o);
11134         }catch(e){
11135             this.fireEvent("loadexception", this, o, trans.arg, e);
11136             trans.callback.call(trans.scope||window, null, trans.arg, false);
11137             return;
11138         }
11139         this.fireEvent("load", this, o, trans.arg);
11140         trans.callback.call(trans.scope||window, result, trans.arg, true);
11141     },
11142
11143     // private
11144     handleFailure : function(trans){
11145         this.trans = false;
11146         this.destroyTrans(trans, false);
11147         this.fireEvent("loadexception", this, null, trans.arg);
11148         trans.callback.call(trans.scope||window, null, trans.arg, false);
11149     }
11150 });/*
11151  * Based on:
11152  * Ext JS Library 1.1.1
11153  * Copyright(c) 2006-2007, Ext JS, LLC.
11154  *
11155  * Originally Released Under LGPL - original licence link has changed is not relivant.
11156  *
11157  * Fork - LGPL
11158  * <script type="text/javascript">
11159  */
11160
11161 /**
11162  * @class Roo.data.JsonReader
11163  * @extends Roo.data.DataReader
11164  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11165  * based on mappings in a provided Roo.data.Record constructor.
11166  * 
11167  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11168  * in the reply previously. 
11169  * 
11170  * <p>
11171  * Example code:
11172  * <pre><code>
11173 var RecordDef = Roo.data.Record.create([
11174     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11175     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11176 ]);
11177 var myReader = new Roo.data.JsonReader({
11178     totalProperty: "results",    // The property which contains the total dataset size (optional)
11179     root: "rows",                // The property which contains an Array of row objects
11180     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11181 }, RecordDef);
11182 </code></pre>
11183  * <p>
11184  * This would consume a JSON file like this:
11185  * <pre><code>
11186 { 'results': 2, 'rows': [
11187     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11188     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11189 }
11190 </code></pre>
11191  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11192  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11193  * paged from the remote server.
11194  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11195  * @cfg {String} root name of the property which contains the Array of row objects.
11196  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11197  * @cfg {Array} fields Array of field definition objects
11198  * @constructor
11199  * Create a new JsonReader
11200  * @param {Object} meta Metadata configuration options
11201  * @param {Object} recordType Either an Array of field definition objects,
11202  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11203  */
11204 Roo.data.JsonReader = function(meta, recordType){
11205     
11206     meta = meta || {};
11207     // set some defaults:
11208     Roo.applyIf(meta, {
11209         totalProperty: 'total',
11210         successProperty : 'success',
11211         root : 'data',
11212         id : 'id'
11213     });
11214     
11215     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11216 };
11217 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11218     
11219     /**
11220      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11221      * Used by Store query builder to append _requestMeta to params.
11222      * 
11223      */
11224     metaFromRemote : false,
11225     /**
11226      * This method is only used by a DataProxy which has retrieved data from a remote server.
11227      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11228      * @return {Object} data A data block which is used by an Roo.data.Store object as
11229      * a cache of Roo.data.Records.
11230      */
11231     read : function(response){
11232         var json = response.responseText;
11233        
11234         var o = /* eval:var:o */ eval("("+json+")");
11235         if(!o) {
11236             throw {message: "JsonReader.read: Json object not found"};
11237         }
11238         
11239         if(o.metaData){
11240             
11241             delete this.ef;
11242             this.metaFromRemote = true;
11243             this.meta = o.metaData;
11244             this.recordType = Roo.data.Record.create(o.metaData.fields);
11245             this.onMetaChange(this.meta, this.recordType, o);
11246         }
11247         return this.readRecords(o);
11248     },
11249
11250     // private function a store will implement
11251     onMetaChange : function(meta, recordType, o){
11252
11253     },
11254
11255     /**
11256          * @ignore
11257          */
11258     simpleAccess: function(obj, subsc) {
11259         return obj[subsc];
11260     },
11261
11262         /**
11263          * @ignore
11264          */
11265     getJsonAccessor: function(){
11266         var re = /[\[\.]/;
11267         return function(expr) {
11268             try {
11269                 return(re.test(expr))
11270                     ? new Function("obj", "return obj." + expr)
11271                     : function(obj){
11272                         return obj[expr];
11273                     };
11274             } catch(e){}
11275             return Roo.emptyFn;
11276         };
11277     }(),
11278
11279     /**
11280      * Create a data block containing Roo.data.Records from an XML document.
11281      * @param {Object} o An object which contains an Array of row objects in the property specified
11282      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11283      * which contains the total size of the dataset.
11284      * @return {Object} data A data block which is used by an Roo.data.Store object as
11285      * a cache of Roo.data.Records.
11286      */
11287     readRecords : function(o){
11288         /**
11289          * After any data loads, the raw JSON data is available for further custom processing.
11290          * @type Object
11291          */
11292         this.o = o;
11293         var s = this.meta, Record = this.recordType,
11294             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11295
11296 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11297         if (!this.ef) {
11298             if(s.totalProperty) {
11299                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11300                 }
11301                 if(s.successProperty) {
11302                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11303                 }
11304                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11305                 if (s.id) {
11306                         var g = this.getJsonAccessor(s.id);
11307                         this.getId = function(rec) {
11308                                 var r = g(rec);  
11309                                 return (r === undefined || r === "") ? null : r;
11310                         };
11311                 } else {
11312                         this.getId = function(){return null;};
11313                 }
11314             this.ef = [];
11315             for(var jj = 0; jj < fl; jj++){
11316                 f = fi[jj];
11317                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11318                 this.ef[jj] = this.getJsonAccessor(map);
11319             }
11320         }
11321
11322         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11323         if(s.totalProperty){
11324             var vt = parseInt(this.getTotal(o), 10);
11325             if(!isNaN(vt)){
11326                 totalRecords = vt;
11327             }
11328         }
11329         if(s.successProperty){
11330             var vs = this.getSuccess(o);
11331             if(vs === false || vs === 'false'){
11332                 success = false;
11333             }
11334         }
11335         var records = [];
11336         for(var i = 0; i < c; i++){
11337                 var n = root[i];
11338             var values = {};
11339             var id = this.getId(n);
11340             for(var j = 0; j < fl; j++){
11341                 f = fi[j];
11342             var v = this.ef[j](n);
11343             if (!f.convert) {
11344                 Roo.log('missing convert for ' + f.name);
11345                 Roo.log(f);
11346                 continue;
11347             }
11348             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11349             }
11350             var record = new Record(values, id);
11351             record.json = n;
11352             records[i] = record;
11353         }
11354         return {
11355             raw : o,
11356             success : success,
11357             records : records,
11358             totalRecords : totalRecords
11359         };
11360     }
11361 });/*
11362  * Based on:
11363  * Ext JS Library 1.1.1
11364  * Copyright(c) 2006-2007, Ext JS, LLC.
11365  *
11366  * Originally Released Under LGPL - original licence link has changed is not relivant.
11367  *
11368  * Fork - LGPL
11369  * <script type="text/javascript">
11370  */
11371
11372 /**
11373  * @class Roo.data.ArrayReader
11374  * @extends Roo.data.DataReader
11375  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11376  * Each element of that Array represents a row of data fields. The
11377  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11378  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11379  * <p>
11380  * Example code:.
11381  * <pre><code>
11382 var RecordDef = Roo.data.Record.create([
11383     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11384     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11385 ]);
11386 var myReader = new Roo.data.ArrayReader({
11387     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11388 }, RecordDef);
11389 </code></pre>
11390  * <p>
11391  * This would consume an Array like this:
11392  * <pre><code>
11393 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11394   </code></pre>
11395  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11396  * @constructor
11397  * Create a new JsonReader
11398  * @param {Object} meta Metadata configuration options.
11399  * @param {Object} recordType Either an Array of field definition objects
11400  * as specified to {@link Roo.data.Record#create},
11401  * or an {@link Roo.data.Record} object
11402  * created using {@link Roo.data.Record#create}.
11403  */
11404 Roo.data.ArrayReader = function(meta, recordType){
11405     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11406 };
11407
11408 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11409     /**
11410      * Create a data block containing Roo.data.Records from an XML document.
11411      * @param {Object} o An Array of row objects which represents the dataset.
11412      * @return {Object} data A data block which is used by an Roo.data.Store object as
11413      * a cache of Roo.data.Records.
11414      */
11415     readRecords : function(o){
11416         var sid = this.meta ? this.meta.id : null;
11417         var recordType = this.recordType, fields = recordType.prototype.fields;
11418         var records = [];
11419         var root = o;
11420             for(var i = 0; i < root.length; i++){
11421                     var n = root[i];
11422                 var values = {};
11423                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11424                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11425                 var f = fields.items[j];
11426                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11427                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11428                 v = f.convert(v);
11429                 values[f.name] = v;
11430             }
11431                 var record = new recordType(values, id);
11432                 record.json = n;
11433                 records[records.length] = record;
11434             }
11435             return {
11436                 records : records,
11437                 totalRecords : records.length
11438             };
11439     }
11440 });/*
11441  * - LGPL
11442  * * 
11443  */
11444
11445 /**
11446  * @class Roo.bootstrap.ComboBox
11447  * @extends Roo.bootstrap.TriggerField
11448  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11449  * @cfg {Boolean} append (true|false) default false
11450  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11451  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11452  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11453  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11454  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11455  * @cfg {Boolean} animate default true
11456  * @cfg {Boolean} emptyResultText only for touch device
11457  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11458  * @constructor
11459  * Create a new ComboBox.
11460  * @param {Object} config Configuration options
11461  */
11462 Roo.bootstrap.ComboBox = function(config){
11463     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11464     this.addEvents({
11465         /**
11466          * @event expand
11467          * Fires when the dropdown list is expanded
11468              * @param {Roo.bootstrap.ComboBox} combo This combo box
11469              */
11470         'expand' : true,
11471         /**
11472          * @event collapse
11473          * Fires when the dropdown list is collapsed
11474              * @param {Roo.bootstrap.ComboBox} combo This combo box
11475              */
11476         'collapse' : true,
11477         /**
11478          * @event beforeselect
11479          * Fires before a list item is selected. Return false to cancel the selection.
11480              * @param {Roo.bootstrap.ComboBox} combo This combo box
11481              * @param {Roo.data.Record} record The data record returned from the underlying store
11482              * @param {Number} index The index of the selected item in the dropdown list
11483              */
11484         'beforeselect' : true,
11485         /**
11486          * @event select
11487          * Fires when a list item is selected
11488              * @param {Roo.bootstrap.ComboBox} combo This combo box
11489              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11490              * @param {Number} index The index of the selected item in the dropdown list
11491              */
11492         'select' : true,
11493         /**
11494          * @event beforequery
11495          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11496          * The event object passed has these properties:
11497              * @param {Roo.bootstrap.ComboBox} combo This combo box
11498              * @param {String} query The query
11499              * @param {Boolean} forceAll true to force "all" query
11500              * @param {Boolean} cancel true to cancel the query
11501              * @param {Object} e The query event object
11502              */
11503         'beforequery': true,
11504          /**
11505          * @event add
11506          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11507              * @param {Roo.bootstrap.ComboBox} combo This combo box
11508              */
11509         'add' : true,
11510         /**
11511          * @event edit
11512          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11513              * @param {Roo.bootstrap.ComboBox} combo This combo box
11514              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11515              */
11516         'edit' : true,
11517         /**
11518          * @event remove
11519          * Fires when the remove value from the combobox array
11520              * @param {Roo.bootstrap.ComboBox} combo This combo box
11521              */
11522         'remove' : true,
11523         /**
11524          * @event specialfilter
11525          * Fires when specialfilter
11526             * @param {Roo.bootstrap.ComboBox} combo This combo box
11527             */
11528         'specialfilter' : true,
11529         /**
11530          * @event tick
11531          * Fires when tick the element
11532             * @param {Roo.bootstrap.ComboBox} combo This combo box
11533             */
11534         'tick' : true,
11535         /**
11536          * @event touchviewdisplay
11537          * Fires when touch view require special display (default is using displayField)
11538             * @param {Roo.bootstrap.ComboBox} combo This combo box
11539             * @param {Object} cfg set html .
11540             */
11541         'touchviewdisplay' : true
11542         
11543     });
11544     
11545     this.item = [];
11546     this.tickItems = [];
11547     
11548     this.selectedIndex = -1;
11549     if(this.mode == 'local'){
11550         if(config.queryDelay === undefined){
11551             this.queryDelay = 10;
11552         }
11553         if(config.minChars === undefined){
11554             this.minChars = 0;
11555         }
11556     }
11557 };
11558
11559 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11560      
11561     /**
11562      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11563      * rendering into an Roo.Editor, defaults to false)
11564      */
11565     /**
11566      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11567      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11568      */
11569     /**
11570      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11571      */
11572     /**
11573      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11574      * the dropdown list (defaults to undefined, with no header element)
11575      */
11576
11577      /**
11578      * @cfg {String/Roo.Template} tpl The template to use to render the output
11579      */
11580      
11581      /**
11582      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11583      */
11584     listWidth: undefined,
11585     /**
11586      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11587      * mode = 'remote' or 'text' if mode = 'local')
11588      */
11589     displayField: undefined,
11590     
11591     /**
11592      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11593      * mode = 'remote' or 'value' if mode = 'local'). 
11594      * Note: use of a valueField requires the user make a selection
11595      * in order for a value to be mapped.
11596      */
11597     valueField: undefined,
11598     
11599     
11600     /**
11601      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11602      * field's data value (defaults to the underlying DOM element's name)
11603      */
11604     hiddenName: undefined,
11605     /**
11606      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11607      */
11608     listClass: '',
11609     /**
11610      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11611      */
11612     selectedClass: 'active',
11613     
11614     /**
11615      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11616      */
11617     shadow:'sides',
11618     /**
11619      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11620      * anchor positions (defaults to 'tl-bl')
11621      */
11622     listAlign: 'tl-bl?',
11623     /**
11624      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11625      */
11626     maxHeight: 300,
11627     /**
11628      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11629      * query specified by the allQuery config option (defaults to 'query')
11630      */
11631     triggerAction: 'query',
11632     /**
11633      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11634      * (defaults to 4, does not apply if editable = false)
11635      */
11636     minChars : 4,
11637     /**
11638      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11639      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11640      */
11641     typeAhead: false,
11642     /**
11643      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11644      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11645      */
11646     queryDelay: 500,
11647     /**
11648      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11649      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11650      */
11651     pageSize: 0,
11652     /**
11653      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11654      * when editable = true (defaults to false)
11655      */
11656     selectOnFocus:false,
11657     /**
11658      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11659      */
11660     queryParam: 'query',
11661     /**
11662      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11663      * when mode = 'remote' (defaults to 'Loading...')
11664      */
11665     loadingText: 'Loading...',
11666     /**
11667      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11668      */
11669     resizable: false,
11670     /**
11671      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11672      */
11673     handleHeight : 8,
11674     /**
11675      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11676      * traditional select (defaults to true)
11677      */
11678     editable: true,
11679     /**
11680      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11681      */
11682     allQuery: '',
11683     /**
11684      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11685      */
11686     mode: 'remote',
11687     /**
11688      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11689      * listWidth has a higher value)
11690      */
11691     minListWidth : 70,
11692     /**
11693      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11694      * allow the user to set arbitrary text into the field (defaults to false)
11695      */
11696     forceSelection:false,
11697     /**
11698      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11699      * if typeAhead = true (defaults to 250)
11700      */
11701     typeAheadDelay : 250,
11702     /**
11703      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11704      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11705      */
11706     valueNotFoundText : undefined,
11707     /**
11708      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11709      */
11710     blockFocus : false,
11711     
11712     /**
11713      * @cfg {Boolean} disableClear Disable showing of clear button.
11714      */
11715     disableClear : false,
11716     /**
11717      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11718      */
11719     alwaysQuery : false,
11720     
11721     /**
11722      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11723      */
11724     multiple : false,
11725     
11726     /**
11727      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11728      */
11729     invalidClass : "has-warning",
11730     
11731     /**
11732      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11733      */
11734     validClass : "has-success",
11735     
11736     /**
11737      * @cfg {Boolean} specialFilter (true|false) special filter default false
11738      */
11739     specialFilter : false,
11740     
11741     /**
11742      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11743      */
11744     mobileTouchView : true,
11745     
11746     //private
11747     addicon : false,
11748     editicon: false,
11749     
11750     page: 0,
11751     hasQuery: false,
11752     append: false,
11753     loadNext: false,
11754     autoFocus : true,
11755     tickable : false,
11756     btnPosition : 'right',
11757     triggerList : true,
11758     showToggleBtn : true,
11759     animate : true,
11760     emptyResultText: 'Empty',
11761     triggerText : 'Select',
11762     
11763     // element that contains real text value.. (when hidden is used..)
11764     
11765     getAutoCreate : function()
11766     {
11767         var cfg = false;
11768         
11769         /*
11770          * Touch Devices
11771          */
11772         
11773         if(Roo.isTouch && this.mobileTouchView){
11774             cfg = this.getAutoCreateTouchView();
11775             return cfg;;
11776         }
11777         
11778         /*
11779          *  Normal ComboBox
11780          */
11781         if(!this.tickable){
11782             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11783             return cfg;
11784         }
11785         
11786         /*
11787          *  ComboBox with tickable selections
11788          */
11789              
11790         var align = this.labelAlign || this.parentLabelAlign();
11791         
11792         cfg = {
11793             cls : 'form-group roo-combobox-tickable' //input-group
11794         };
11795         
11796         var buttons = {
11797             tag : 'div',
11798             cls : 'tickable-buttons',
11799             cn : [
11800                 {
11801                     tag : 'button',
11802                     type : 'button',
11803                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11804                     html : this.triggerText
11805                 },
11806                 {
11807                     tag : 'button',
11808                     type : 'button',
11809                     name : 'ok',
11810                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11811                     html : 'Done'
11812                 },
11813                 {
11814                     tag : 'button',
11815                     type : 'button',
11816                     name : 'cancel',
11817                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11818                     html : 'Cancel'
11819                 }
11820             ]
11821         };
11822         
11823         if(this.editable){
11824             buttons.cn.unshift({
11825                 tag: 'input',
11826                 cls: 'select2-search-field-input'
11827             });
11828         }
11829         
11830         var _this = this;
11831         
11832         Roo.each(buttons.cn, function(c){
11833             if (_this.size) {
11834                 c.cls += ' btn-' + _this.size;
11835             }
11836
11837             if (_this.disabled) {
11838                 c.disabled = true;
11839             }
11840         });
11841         
11842         var box = {
11843             tag: 'div',
11844             cn: [
11845                 {
11846                     tag: 'input',
11847                     type : 'hidden',
11848                     cls: 'form-hidden-field'
11849                 },
11850                 {
11851                     tag: 'ul',
11852                     cls: 'select2-choices',
11853                     cn:[
11854                         {
11855                             tag: 'li',
11856                             cls: 'select2-search-field',
11857                             cn: [
11858
11859                                 buttons
11860                             ]
11861                         }
11862                     ]
11863                 }
11864             ]
11865         };
11866         
11867         var combobox = {
11868             cls: 'select2-container input-group select2-container-multi',
11869             cn: [
11870                 box
11871 //                {
11872 //                    tag: 'ul',
11873 //                    cls: 'typeahead typeahead-long dropdown-menu',
11874 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11875 //                }
11876             ]
11877         };
11878         
11879         if(this.hasFeedback && !this.allowBlank){
11880             
11881             var feedback = {
11882                 tag: 'span',
11883                 cls: 'glyphicon form-control-feedback'
11884             };
11885
11886             combobox.cn.push(feedback);
11887         }
11888         
11889         if (align ==='left' && this.fieldLabel.length) {
11890             
11891 //                Roo.log("left and has label");
11892                 cfg.cn = [
11893                     
11894                     {
11895                         tag: 'label',
11896                         'for' :  id,
11897                         cls : 'control-label col-sm-' + this.labelWidth,
11898                         html : this.fieldLabel
11899                         
11900                     },
11901                     {
11902                         cls : "col-sm-" + (12 - this.labelWidth), 
11903                         cn: [
11904                             combobox
11905                         ]
11906                     }
11907                     
11908                 ];
11909         } else if ( this.fieldLabel.length) {
11910 //                Roo.log(" label");
11911                  cfg.cn = [
11912                    
11913                     {
11914                         tag: 'label',
11915                         //cls : 'input-group-addon',
11916                         html : this.fieldLabel
11917                         
11918                     },
11919                     
11920                     combobox
11921                     
11922                 ];
11923
11924         } else {
11925             
11926 //                Roo.log(" no label && no align");
11927                 cfg = combobox
11928                      
11929                 
11930         }
11931          
11932         var settings=this;
11933         ['xs','sm','md','lg'].map(function(size){
11934             if (settings[size]) {
11935                 cfg.cls += ' col-' + size + '-' + settings[size];
11936             }
11937         });
11938         
11939         return cfg;
11940         
11941     },
11942     
11943     _initEventsCalled : false,
11944     
11945     // private
11946     initEvents: function()
11947     {
11948         
11949         if (this._initEventsCalled) { // as we call render... prevent looping...
11950             return;
11951         }
11952         this._initEventsCalled = true;
11953         
11954         if (!this.store) {
11955             throw "can not find store for combo";
11956         }
11957         
11958         this.store = Roo.factory(this.store, Roo.data);
11959         
11960         // if we are building from html. then this element is so complex, that we can not really
11961         // use the rendered HTML.
11962         // so we have to trash and replace the previous code.
11963         if (Roo.XComponent.build_from_html) {
11964             
11965             // remove this element....
11966             var e = this.el.dom, k=0;
11967             while (e ) { e = e.previousSibling;  ++k;}
11968
11969             this.el.remove();
11970             
11971             this.el=false;
11972             this.rendered = false;
11973             
11974             this.render(this.parent().getChildContainer(true), k);
11975             
11976             
11977             
11978         }
11979         
11980         
11981         /*
11982          * Touch Devices
11983          */
11984         
11985         if(Roo.isTouch && this.mobileTouchView){
11986             this.initTouchView();
11987             return;
11988         }
11989         
11990         if(this.tickable){
11991             this.initTickableEvents();
11992             return;
11993         }
11994         
11995         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11996         
11997         if(this.hiddenName){
11998             
11999             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12000             
12001             this.hiddenField.dom.value =
12002                 this.hiddenValue !== undefined ? this.hiddenValue :
12003                 this.value !== undefined ? this.value : '';
12004
12005             // prevent input submission
12006             this.el.dom.removeAttribute('name');
12007             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12008              
12009              
12010         }
12011         //if(Roo.isGecko){
12012         //    this.el.dom.setAttribute('autocomplete', 'off');
12013         //}
12014         
12015         var cls = 'x-combo-list';
12016         
12017         //this.list = new Roo.Layer({
12018         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12019         //});
12020         
12021         var _this = this;
12022         
12023         (function(){
12024             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12025             _this.list.setWidth(lw);
12026         }).defer(100);
12027         
12028         this.list.on('mouseover', this.onViewOver, this);
12029         this.list.on('mousemove', this.onViewMove, this);
12030         
12031         this.list.on('scroll', this.onViewScroll, this);
12032         
12033         /*
12034         this.list.swallowEvent('mousewheel');
12035         this.assetHeight = 0;
12036
12037         if(this.title){
12038             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12039             this.assetHeight += this.header.getHeight();
12040         }
12041
12042         this.innerList = this.list.createChild({cls:cls+'-inner'});
12043         this.innerList.on('mouseover', this.onViewOver, this);
12044         this.innerList.on('mousemove', this.onViewMove, this);
12045         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12046         
12047         if(this.allowBlank && !this.pageSize && !this.disableClear){
12048             this.footer = this.list.createChild({cls:cls+'-ft'});
12049             this.pageTb = new Roo.Toolbar(this.footer);
12050            
12051         }
12052         if(this.pageSize){
12053             this.footer = this.list.createChild({cls:cls+'-ft'});
12054             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12055                     {pageSize: this.pageSize});
12056             
12057         }
12058         
12059         if (this.pageTb && this.allowBlank && !this.disableClear) {
12060             var _this = this;
12061             this.pageTb.add(new Roo.Toolbar.Fill(), {
12062                 cls: 'x-btn-icon x-btn-clear',
12063                 text: '&#160;',
12064                 handler: function()
12065                 {
12066                     _this.collapse();
12067                     _this.clearValue();
12068                     _this.onSelect(false, -1);
12069                 }
12070             });
12071         }
12072         if (this.footer) {
12073             this.assetHeight += this.footer.getHeight();
12074         }
12075         */
12076             
12077         if(!this.tpl){
12078             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12079         }
12080
12081         this.view = new Roo.View(this.list, this.tpl, {
12082             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12083         });
12084         //this.view.wrapEl.setDisplayed(false);
12085         this.view.on('click', this.onViewClick, this);
12086         
12087         
12088         
12089         this.store.on('beforeload', this.onBeforeLoad, this);
12090         this.store.on('load', this.onLoad, this);
12091         this.store.on('loadexception', this.onLoadException, this);
12092         /*
12093         if(this.resizable){
12094             this.resizer = new Roo.Resizable(this.list,  {
12095                pinned:true, handles:'se'
12096             });
12097             this.resizer.on('resize', function(r, w, h){
12098                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12099                 this.listWidth = w;
12100                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12101                 this.restrictHeight();
12102             }, this);
12103             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12104         }
12105         */
12106         if(!this.editable){
12107             this.editable = true;
12108             this.setEditable(false);
12109         }
12110         
12111         /*
12112         
12113         if (typeof(this.events.add.listeners) != 'undefined') {
12114             
12115             this.addicon = this.wrap.createChild(
12116                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12117        
12118             this.addicon.on('click', function(e) {
12119                 this.fireEvent('add', this);
12120             }, this);
12121         }
12122         if (typeof(this.events.edit.listeners) != 'undefined') {
12123             
12124             this.editicon = this.wrap.createChild(
12125                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12126             if (this.addicon) {
12127                 this.editicon.setStyle('margin-left', '40px');
12128             }
12129             this.editicon.on('click', function(e) {
12130                 
12131                 // we fire even  if inothing is selected..
12132                 this.fireEvent('edit', this, this.lastData );
12133                 
12134             }, this);
12135         }
12136         */
12137         
12138         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12139             "up" : function(e){
12140                 this.inKeyMode = true;
12141                 this.selectPrev();
12142             },
12143
12144             "down" : function(e){
12145                 if(!this.isExpanded()){
12146                     this.onTriggerClick();
12147                 }else{
12148                     this.inKeyMode = true;
12149                     this.selectNext();
12150                 }
12151             },
12152
12153             "enter" : function(e){
12154 //                this.onViewClick();
12155                 //return true;
12156                 this.collapse();
12157                 
12158                 if(this.fireEvent("specialkey", this, e)){
12159                     this.onViewClick(false);
12160                 }
12161                 
12162                 return true;
12163             },
12164
12165             "esc" : function(e){
12166                 this.collapse();
12167             },
12168
12169             "tab" : function(e){
12170                 this.collapse();
12171                 
12172                 if(this.fireEvent("specialkey", this, e)){
12173                     this.onViewClick(false);
12174                 }
12175                 
12176                 return true;
12177             },
12178
12179             scope : this,
12180
12181             doRelay : function(foo, bar, hname){
12182                 if(hname == 'down' || this.scope.isExpanded()){
12183                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12184                 }
12185                 return true;
12186             },
12187
12188             forceKeyDown: true
12189         });
12190         
12191         
12192         this.queryDelay = Math.max(this.queryDelay || 10,
12193                 this.mode == 'local' ? 10 : 250);
12194         
12195         
12196         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12197         
12198         if(this.typeAhead){
12199             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12200         }
12201         if(this.editable !== false){
12202             this.inputEl().on("keyup", this.onKeyUp, this);
12203         }
12204         if(this.forceSelection){
12205             this.inputEl().on('blur', this.doForce, this);
12206         }
12207         
12208         if(this.multiple){
12209             this.choices = this.el.select('ul.select2-choices', true).first();
12210             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12211         }
12212     },
12213     
12214     initTickableEvents: function()
12215     {   
12216         this.createList();
12217         
12218         if(this.hiddenName){
12219             
12220             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12221             
12222             this.hiddenField.dom.value =
12223                 this.hiddenValue !== undefined ? this.hiddenValue :
12224                 this.value !== undefined ? this.value : '';
12225
12226             // prevent input submission
12227             this.el.dom.removeAttribute('name');
12228             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12229              
12230              
12231         }
12232         
12233 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12234         
12235         this.choices = this.el.select('ul.select2-choices', true).first();
12236         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12237         if(this.triggerList){
12238             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12239         }
12240          
12241         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12242         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12243         
12244         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12245         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12246         
12247         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12248         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12249         
12250         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12251         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12252         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12253         
12254         this.okBtn.hide();
12255         this.cancelBtn.hide();
12256         
12257         var _this = this;
12258         
12259         (function(){
12260             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12261             _this.list.setWidth(lw);
12262         }).defer(100);
12263         
12264         this.list.on('mouseover', this.onViewOver, this);
12265         this.list.on('mousemove', this.onViewMove, this);
12266         
12267         this.list.on('scroll', this.onViewScroll, this);
12268         
12269         if(!this.tpl){
12270             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>';
12271         }
12272
12273         this.view = new Roo.View(this.list, this.tpl, {
12274             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12275         });
12276         
12277         //this.view.wrapEl.setDisplayed(false);
12278         this.view.on('click', this.onViewClick, this);
12279         
12280         
12281         
12282         this.store.on('beforeload', this.onBeforeLoad, this);
12283         this.store.on('load', this.onLoad, this);
12284         this.store.on('loadexception', this.onLoadException, this);
12285         
12286         if(this.editable){
12287             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12288                 "up" : function(e){
12289                     this.inKeyMode = true;
12290                     this.selectPrev();
12291                 },
12292
12293                 "down" : function(e){
12294                     this.inKeyMode = true;
12295                     this.selectNext();
12296                 },
12297
12298                 "enter" : function(e){
12299                     if(this.fireEvent("specialkey", this, e)){
12300                         this.onViewClick(false);
12301                     }
12302                     
12303                     return true;
12304                 },
12305
12306                 "esc" : function(e){
12307                     this.onTickableFooterButtonClick(e, false, false);
12308                 },
12309
12310                 "tab" : function(e){
12311                     this.fireEvent("specialkey", this, e);
12312                     
12313                     this.onTickableFooterButtonClick(e, false, false);
12314                     
12315                     return true;
12316                 },
12317
12318                 scope : this,
12319
12320                 doRelay : function(e, fn, key){
12321                     if(this.scope.isExpanded()){
12322                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12323                     }
12324                     return true;
12325                 },
12326
12327                 forceKeyDown: true
12328             });
12329         }
12330         
12331         this.queryDelay = Math.max(this.queryDelay || 10,
12332                 this.mode == 'local' ? 10 : 250);
12333         
12334         
12335         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12336         
12337         if(this.typeAhead){
12338             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12339         }
12340         
12341         if(this.editable !== false){
12342             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12343         }
12344         
12345     },
12346
12347     onDestroy : function(){
12348         if(this.view){
12349             this.view.setStore(null);
12350             this.view.el.removeAllListeners();
12351             this.view.el.remove();
12352             this.view.purgeListeners();
12353         }
12354         if(this.list){
12355             this.list.dom.innerHTML  = '';
12356         }
12357         
12358         if(this.store){
12359             this.store.un('beforeload', this.onBeforeLoad, this);
12360             this.store.un('load', this.onLoad, this);
12361             this.store.un('loadexception', this.onLoadException, this);
12362         }
12363         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12364     },
12365
12366     // private
12367     fireKey : function(e){
12368         if(e.isNavKeyPress() && !this.list.isVisible()){
12369             this.fireEvent("specialkey", this, e);
12370         }
12371     },
12372
12373     // private
12374     onResize: function(w, h){
12375 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12376 //        
12377 //        if(typeof w != 'number'){
12378 //            // we do not handle it!?!?
12379 //            return;
12380 //        }
12381 //        var tw = this.trigger.getWidth();
12382 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12383 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12384 //        var x = w - tw;
12385 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12386 //            
12387 //        //this.trigger.setStyle('left', x+'px');
12388 //        
12389 //        if(this.list && this.listWidth === undefined){
12390 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12391 //            this.list.setWidth(lw);
12392 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12393 //        }
12394         
12395     
12396         
12397     },
12398
12399     /**
12400      * Allow or prevent the user from directly editing the field text.  If false is passed,
12401      * the user will only be able to select from the items defined in the dropdown list.  This method
12402      * is the runtime equivalent of setting the 'editable' config option at config time.
12403      * @param {Boolean} value True to allow the user to directly edit the field text
12404      */
12405     setEditable : function(value){
12406         if(value == this.editable){
12407             return;
12408         }
12409         this.editable = value;
12410         if(!value){
12411             this.inputEl().dom.setAttribute('readOnly', true);
12412             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12413             this.inputEl().addClass('x-combo-noedit');
12414         }else{
12415             this.inputEl().dom.setAttribute('readOnly', false);
12416             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12417             this.inputEl().removeClass('x-combo-noedit');
12418         }
12419     },
12420
12421     // private
12422     
12423     onBeforeLoad : function(combo,opts){
12424         if(!this.hasFocus){
12425             return;
12426         }
12427          if (!opts.add) {
12428             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12429          }
12430         this.restrictHeight();
12431         this.selectedIndex = -1;
12432     },
12433
12434     // private
12435     onLoad : function(){
12436         
12437         this.hasQuery = false;
12438         
12439         if(!this.hasFocus){
12440             return;
12441         }
12442         
12443         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12444             this.loading.hide();
12445         }
12446              
12447         if(this.store.getCount() > 0){
12448             this.expand();
12449             this.restrictHeight();
12450             if(this.lastQuery == this.allQuery){
12451                 if(this.editable && !this.tickable){
12452                     this.inputEl().dom.select();
12453                 }
12454                 
12455                 if(
12456                     !this.selectByValue(this.value, true) &&
12457                     this.autoFocus && 
12458                     (
12459                         !this.store.lastOptions ||
12460                         typeof(this.store.lastOptions.add) == 'undefined' || 
12461                         this.store.lastOptions.add != true
12462                     )
12463                 ){
12464                     this.select(0, true);
12465                 }
12466             }else{
12467                 if(this.autoFocus){
12468                     this.selectNext();
12469                 }
12470                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12471                     this.taTask.delay(this.typeAheadDelay);
12472                 }
12473             }
12474         }else{
12475             this.onEmptyResults();
12476         }
12477         
12478         //this.el.focus();
12479     },
12480     // private
12481     onLoadException : function()
12482     {
12483         this.hasQuery = false;
12484         
12485         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12486             this.loading.hide();
12487         }
12488         
12489         if(this.tickable && this.editable){
12490             return;
12491         }
12492         
12493         this.collapse();
12494         // only causes errors at present
12495         //Roo.log(this.store.reader.jsonData);
12496         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12497             // fixme
12498             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12499         //}
12500         
12501         
12502     },
12503     // private
12504     onTypeAhead : function(){
12505         if(this.store.getCount() > 0){
12506             var r = this.store.getAt(0);
12507             var newValue = r.data[this.displayField];
12508             var len = newValue.length;
12509             var selStart = this.getRawValue().length;
12510             
12511             if(selStart != len){
12512                 this.setRawValue(newValue);
12513                 this.selectText(selStart, newValue.length);
12514             }
12515         }
12516     },
12517
12518     // private
12519     onSelect : function(record, index){
12520         
12521         if(this.fireEvent('beforeselect', this, record, index) !== false){
12522         
12523             this.setFromData(index > -1 ? record.data : false);
12524             
12525             this.collapse();
12526             this.fireEvent('select', this, record, index);
12527         }
12528     },
12529
12530     /**
12531      * Returns the currently selected field value or empty string if no value is set.
12532      * @return {String} value The selected value
12533      */
12534     getValue : function(){
12535         
12536         if(this.multiple){
12537             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12538         }
12539         
12540         if(this.valueField){
12541             return typeof this.value != 'undefined' ? this.value : '';
12542         }else{
12543             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12544         }
12545     },
12546
12547     /**
12548      * Clears any text/value currently set in the field
12549      */
12550     clearValue : function(){
12551         if(this.hiddenField){
12552             this.hiddenField.dom.value = '';
12553         }
12554         this.value = '';
12555         this.setRawValue('');
12556         this.lastSelectionText = '';
12557         this.lastData = false;
12558         
12559         var close = this.closeTriggerEl();
12560         
12561         if(close){
12562             close.hide();
12563         }
12564         
12565     },
12566
12567     /**
12568      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12569      * will be displayed in the field.  If the value does not match the data value of an existing item,
12570      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12571      * Otherwise the field will be blank (although the value will still be set).
12572      * @param {String} value The value to match
12573      */
12574     setValue : function(v){
12575         if(this.multiple){
12576             this.syncValue();
12577             return;
12578         }
12579         
12580         var text = v;
12581         if(this.valueField){
12582             var r = this.findRecord(this.valueField, v);
12583             if(r){
12584                 text = r.data[this.displayField];
12585             }else if(this.valueNotFoundText !== undefined){
12586                 text = this.valueNotFoundText;
12587             }
12588         }
12589         this.lastSelectionText = text;
12590         if(this.hiddenField){
12591             this.hiddenField.dom.value = v;
12592         }
12593         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12594         this.value = v;
12595         
12596         var close = this.closeTriggerEl();
12597         
12598         if(close){
12599             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12600         }
12601     },
12602     /**
12603      * @property {Object} the last set data for the element
12604      */
12605     
12606     lastData : false,
12607     /**
12608      * Sets the value of the field based on a object which is related to the record format for the store.
12609      * @param {Object} value the value to set as. or false on reset?
12610      */
12611     setFromData : function(o){
12612         
12613         if(this.multiple){
12614             this.addItem(o);
12615             return;
12616         }
12617             
12618         var dv = ''; // display value
12619         var vv = ''; // value value..
12620         this.lastData = o;
12621         if (this.displayField) {
12622             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12623         } else {
12624             // this is an error condition!!!
12625             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12626         }
12627         
12628         if(this.valueField){
12629             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12630         }
12631         
12632         var close = this.closeTriggerEl();
12633         
12634         if(close){
12635             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12636         }
12637         
12638         if(this.hiddenField){
12639             this.hiddenField.dom.value = vv;
12640             
12641             this.lastSelectionText = dv;
12642             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12643             this.value = vv;
12644             return;
12645         }
12646         // no hidden field.. - we store the value in 'value', but still display
12647         // display field!!!!
12648         this.lastSelectionText = dv;
12649         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12650         this.value = vv;
12651         
12652         
12653         
12654     },
12655     // private
12656     reset : function(){
12657         // overridden so that last data is reset..
12658         
12659         if(this.multiple){
12660             this.clearItem();
12661             return;
12662         }
12663         
12664         this.setValue(this.originalValue);
12665         this.clearInvalid();
12666         this.lastData = false;
12667         if (this.view) {
12668             this.view.clearSelections();
12669         }
12670     },
12671     // private
12672     findRecord : function(prop, value){
12673         var record;
12674         if(this.store.getCount() > 0){
12675             this.store.each(function(r){
12676                 if(r.data[prop] == value){
12677                     record = r;
12678                     return false;
12679                 }
12680                 return true;
12681             });
12682         }
12683         return record;
12684     },
12685     
12686     getName: function()
12687     {
12688         // returns hidden if it's set..
12689         if (!this.rendered) {return ''};
12690         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12691         
12692     },
12693     // private
12694     onViewMove : function(e, t){
12695         this.inKeyMode = false;
12696     },
12697
12698     // private
12699     onViewOver : function(e, t){
12700         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12701             return;
12702         }
12703         var item = this.view.findItemFromChild(t);
12704         
12705         if(item){
12706             var index = this.view.indexOf(item);
12707             this.select(index, false);
12708         }
12709     },
12710
12711     // private
12712     onViewClick : function(view, doFocus, el, e)
12713     {
12714         var index = this.view.getSelectedIndexes()[0];
12715         
12716         var r = this.store.getAt(index);
12717         
12718         if(this.tickable){
12719             
12720             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12721                 return;
12722             }
12723             
12724             var rm = false;
12725             var _this = this;
12726             
12727             Roo.each(this.tickItems, function(v,k){
12728                 
12729                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12730                     Roo.log(v);
12731                     _this.tickItems.splice(k, 1);
12732                     
12733                     if(typeof(e) == 'undefined' && view == false){
12734                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12735                     }
12736                     
12737                     rm = true;
12738                     return;
12739                 }
12740             });
12741             
12742             if(rm){
12743                 return;
12744             }
12745             
12746             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12747                 this.tickItems.push(r.data);
12748             }
12749             
12750             if(typeof(e) == 'undefined' && view == false){
12751                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12752             }
12753                     
12754             return;
12755         }
12756         
12757         if(r){
12758             this.onSelect(r, index);
12759         }
12760         if(doFocus !== false && !this.blockFocus){
12761             this.inputEl().focus();
12762         }
12763     },
12764
12765     // private
12766     restrictHeight : function(){
12767         //this.innerList.dom.style.height = '';
12768         //var inner = this.innerList.dom;
12769         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12770         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12771         //this.list.beginUpdate();
12772         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12773         this.list.alignTo(this.inputEl(), this.listAlign);
12774         this.list.alignTo(this.inputEl(), this.listAlign);
12775         //this.list.endUpdate();
12776     },
12777
12778     // private
12779     onEmptyResults : function(){
12780         
12781         if(this.tickable && this.editable){
12782             this.restrictHeight();
12783             return;
12784         }
12785         
12786         this.collapse();
12787     },
12788
12789     /**
12790      * Returns true if the dropdown list is expanded, else false.
12791      */
12792     isExpanded : function(){
12793         return this.list.isVisible();
12794     },
12795
12796     /**
12797      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12798      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12799      * @param {String} value The data value of the item to select
12800      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12801      * selected item if it is not currently in view (defaults to true)
12802      * @return {Boolean} True if the value matched an item in the list, else false
12803      */
12804     selectByValue : function(v, scrollIntoView){
12805         if(v !== undefined && v !== null){
12806             var r = this.findRecord(this.valueField || this.displayField, v);
12807             if(r){
12808                 this.select(this.store.indexOf(r), scrollIntoView);
12809                 return true;
12810             }
12811         }
12812         return false;
12813     },
12814
12815     /**
12816      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12817      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12818      * @param {Number} index The zero-based index of the list item to select
12819      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12820      * selected item if it is not currently in view (defaults to true)
12821      */
12822     select : function(index, scrollIntoView){
12823         this.selectedIndex = index;
12824         this.view.select(index);
12825         if(scrollIntoView !== false){
12826             var el = this.view.getNode(index);
12827             /*
12828              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12829              */
12830             if(el){
12831                 this.list.scrollChildIntoView(el, false);
12832             }
12833         }
12834     },
12835
12836     // private
12837     selectNext : function(){
12838         var ct = this.store.getCount();
12839         if(ct > 0){
12840             if(this.selectedIndex == -1){
12841                 this.select(0);
12842             }else if(this.selectedIndex < ct-1){
12843                 this.select(this.selectedIndex+1);
12844             }
12845         }
12846     },
12847
12848     // private
12849     selectPrev : function(){
12850         var ct = this.store.getCount();
12851         if(ct > 0){
12852             if(this.selectedIndex == -1){
12853                 this.select(0);
12854             }else if(this.selectedIndex != 0){
12855                 this.select(this.selectedIndex-1);
12856             }
12857         }
12858     },
12859
12860     // private
12861     onKeyUp : function(e){
12862         if(this.editable !== false && !e.isSpecialKey()){
12863             this.lastKey = e.getKey();
12864             this.dqTask.delay(this.queryDelay);
12865         }
12866     },
12867
12868     // private
12869     validateBlur : function(){
12870         return !this.list || !this.list.isVisible();   
12871     },
12872
12873     // private
12874     initQuery : function(){
12875         
12876         var v = this.getRawValue();
12877         
12878         if(this.tickable && this.editable){
12879             v = this.tickableInputEl().getValue();
12880         }
12881         
12882         this.doQuery(v);
12883     },
12884
12885     // private
12886     doForce : function(){
12887         if(this.inputEl().dom.value.length > 0){
12888             this.inputEl().dom.value =
12889                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12890              
12891         }
12892     },
12893
12894     /**
12895      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12896      * query allowing the query action to be canceled if needed.
12897      * @param {String} query The SQL query to execute
12898      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12899      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12900      * saved in the current store (defaults to false)
12901      */
12902     doQuery : function(q, forceAll){
12903         
12904         if(q === undefined || q === null){
12905             q = '';
12906         }
12907         var qe = {
12908             query: q,
12909             forceAll: forceAll,
12910             combo: this,
12911             cancel:false
12912         };
12913         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12914             return false;
12915         }
12916         q = qe.query;
12917         
12918         forceAll = qe.forceAll;
12919         if(forceAll === true || (q.length >= this.minChars)){
12920             
12921             this.hasQuery = true;
12922             
12923             if(this.lastQuery != q || this.alwaysQuery){
12924                 this.lastQuery = q;
12925                 if(this.mode == 'local'){
12926                     this.selectedIndex = -1;
12927                     if(forceAll){
12928                         this.store.clearFilter();
12929                     }else{
12930                         
12931                         if(this.specialFilter){
12932                             this.fireEvent('specialfilter', this);
12933                             this.onLoad();
12934                             return;
12935                         }
12936                         
12937                         this.store.filter(this.displayField, q);
12938                     }
12939                     
12940                     this.store.fireEvent("datachanged", this.store);
12941                     
12942                     this.onLoad();
12943                     
12944                     
12945                 }else{
12946                     
12947                     this.store.baseParams[this.queryParam] = q;
12948                     
12949                     var options = {params : this.getParams(q)};
12950                     
12951                     if(this.loadNext){
12952                         options.add = true;
12953                         options.params.start = this.page * this.pageSize;
12954                     }
12955                     
12956                     this.store.load(options);
12957                     
12958                     /*
12959                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12960                      *  we should expand the list on onLoad
12961                      *  so command out it
12962                      */
12963 //                    this.expand();
12964                 }
12965             }else{
12966                 this.selectedIndex = -1;
12967                 this.onLoad();   
12968             }
12969         }
12970         
12971         this.loadNext = false;
12972     },
12973     
12974     // private
12975     getParams : function(q){
12976         var p = {};
12977         //p[this.queryParam] = q;
12978         
12979         if(this.pageSize){
12980             p.start = 0;
12981             p.limit = this.pageSize;
12982         }
12983         return p;
12984     },
12985
12986     /**
12987      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12988      */
12989     collapse : function(){
12990         if(!this.isExpanded()){
12991             return;
12992         }
12993         
12994         this.list.hide();
12995         
12996         if(this.tickable){
12997             this.hasFocus = false;
12998             this.okBtn.hide();
12999             this.cancelBtn.hide();
13000             this.trigger.show();
13001             
13002             if(this.editable){
13003                 this.tickableInputEl().dom.value = '';
13004                 this.tickableInputEl().blur();
13005             }
13006             
13007         }
13008         
13009         Roo.get(document).un('mousedown', this.collapseIf, this);
13010         Roo.get(document).un('mousewheel', this.collapseIf, this);
13011         if (!this.editable) {
13012             Roo.get(document).un('keydown', this.listKeyPress, this);
13013         }
13014         this.fireEvent('collapse', this);
13015     },
13016
13017     // private
13018     collapseIf : function(e){
13019         var in_combo  = e.within(this.el);
13020         var in_list =  e.within(this.list);
13021         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13022         
13023         if (in_combo || in_list || is_list) {
13024             //e.stopPropagation();
13025             return;
13026         }
13027         
13028         if(this.tickable){
13029             this.onTickableFooterButtonClick(e, false, false);
13030         }
13031
13032         this.collapse();
13033         
13034     },
13035
13036     /**
13037      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13038      */
13039     expand : function(){
13040        
13041         if(this.isExpanded() || !this.hasFocus){
13042             return;
13043         }
13044         
13045         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13046         this.list.setWidth(lw);
13047         
13048         
13049          Roo.log('expand');
13050         
13051         this.list.show();
13052         
13053         this.restrictHeight();
13054         
13055         if(this.tickable){
13056             
13057             this.tickItems = Roo.apply([], this.item);
13058             
13059             this.okBtn.show();
13060             this.cancelBtn.show();
13061             this.trigger.hide();
13062             
13063             if(this.editable){
13064                 this.tickableInputEl().focus();
13065             }
13066             
13067         }
13068         
13069         Roo.get(document).on('mousedown', this.collapseIf, this);
13070         Roo.get(document).on('mousewheel', this.collapseIf, this);
13071         if (!this.editable) {
13072             Roo.get(document).on('keydown', this.listKeyPress, this);
13073         }
13074         
13075         this.fireEvent('expand', this);
13076     },
13077
13078     // private
13079     // Implements the default empty TriggerField.onTriggerClick function
13080     onTriggerClick : function(e)
13081     {
13082         Roo.log('trigger click');
13083         
13084         if(this.disabled || !this.triggerList){
13085             return;
13086         }
13087         
13088         this.page = 0;
13089         this.loadNext = false;
13090         
13091         if(this.isExpanded()){
13092             this.collapse();
13093             if (!this.blockFocus) {
13094                 this.inputEl().focus();
13095             }
13096             
13097         }else {
13098             this.hasFocus = true;
13099             if(this.triggerAction == 'all') {
13100                 this.doQuery(this.allQuery, true);
13101             } else {
13102                 this.doQuery(this.getRawValue());
13103             }
13104             if (!this.blockFocus) {
13105                 this.inputEl().focus();
13106             }
13107         }
13108     },
13109     
13110     onTickableTriggerClick : function(e)
13111     {
13112         if(this.disabled){
13113             return;
13114         }
13115         
13116         this.page = 0;
13117         this.loadNext = false;
13118         this.hasFocus = true;
13119         
13120         if(this.triggerAction == 'all') {
13121             this.doQuery(this.allQuery, true);
13122         } else {
13123             this.doQuery(this.getRawValue());
13124         }
13125     },
13126     
13127     onSearchFieldClick : function(e)
13128     {
13129         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13130             this.onTickableFooterButtonClick(e, false, false);
13131             return;
13132         }
13133         
13134         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13135             return;
13136         }
13137         
13138         this.page = 0;
13139         this.loadNext = false;
13140         this.hasFocus = true;
13141         
13142         if(this.triggerAction == 'all') {
13143             this.doQuery(this.allQuery, true);
13144         } else {
13145             this.doQuery(this.getRawValue());
13146         }
13147     },
13148     
13149     listKeyPress : function(e)
13150     {
13151         //Roo.log('listkeypress');
13152         // scroll to first matching element based on key pres..
13153         if (e.isSpecialKey()) {
13154             return false;
13155         }
13156         var k = String.fromCharCode(e.getKey()).toUpperCase();
13157         //Roo.log(k);
13158         var match  = false;
13159         var csel = this.view.getSelectedNodes();
13160         var cselitem = false;
13161         if (csel.length) {
13162             var ix = this.view.indexOf(csel[0]);
13163             cselitem  = this.store.getAt(ix);
13164             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13165                 cselitem = false;
13166             }
13167             
13168         }
13169         
13170         this.store.each(function(v) { 
13171             if (cselitem) {
13172                 // start at existing selection.
13173                 if (cselitem.id == v.id) {
13174                     cselitem = false;
13175                 }
13176                 return true;
13177             }
13178                 
13179             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13180                 match = this.store.indexOf(v);
13181                 return false;
13182             }
13183             return true;
13184         }, this);
13185         
13186         if (match === false) {
13187             return true; // no more action?
13188         }
13189         // scroll to?
13190         this.view.select(match);
13191         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13192         sn.scrollIntoView(sn.dom.parentNode, false);
13193     },
13194     
13195     onViewScroll : function(e, t){
13196         
13197         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){
13198             return;
13199         }
13200         
13201         this.hasQuery = true;
13202         
13203         this.loading = this.list.select('.loading', true).first();
13204         
13205         if(this.loading === null){
13206             this.list.createChild({
13207                 tag: 'div',
13208                 cls: 'loading select2-more-results select2-active',
13209                 html: 'Loading more results...'
13210             });
13211             
13212             this.loading = this.list.select('.loading', true).first();
13213             
13214             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13215             
13216             this.loading.hide();
13217         }
13218         
13219         this.loading.show();
13220         
13221         var _combo = this;
13222         
13223         this.page++;
13224         this.loadNext = true;
13225         
13226         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13227         
13228         return;
13229     },
13230     
13231     addItem : function(o)
13232     {   
13233         var dv = ''; // display value
13234         
13235         if (this.displayField) {
13236             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13237         } else {
13238             // this is an error condition!!!
13239             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13240         }
13241         
13242         if(!dv.length){
13243             return;
13244         }
13245         
13246         var choice = this.choices.createChild({
13247             tag: 'li',
13248             cls: 'select2-search-choice',
13249             cn: [
13250                 {
13251                     tag: 'div',
13252                     html: dv
13253                 },
13254                 {
13255                     tag: 'a',
13256                     href: '#',
13257                     cls: 'select2-search-choice-close',
13258                     tabindex: '-1'
13259                 }
13260             ]
13261             
13262         }, this.searchField);
13263         
13264         var close = choice.select('a.select2-search-choice-close', true).first();
13265         
13266         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13267         
13268         this.item.push(o);
13269         
13270         this.lastData = o;
13271         
13272         this.syncValue();
13273         
13274         this.inputEl().dom.value = '';
13275         
13276         this.validate();
13277     },
13278     
13279     onRemoveItem : function(e, _self, o)
13280     {
13281         e.preventDefault();
13282         
13283         this.lastItem = Roo.apply([], this.item);
13284         
13285         var index = this.item.indexOf(o.data) * 1;
13286         
13287         if( index < 0){
13288             Roo.log('not this item?!');
13289             return;
13290         }
13291         
13292         this.item.splice(index, 1);
13293         o.item.remove();
13294         
13295         this.syncValue();
13296         
13297         this.fireEvent('remove', this, e);
13298         
13299         this.validate();
13300         
13301     },
13302     
13303     syncValue : function()
13304     {
13305         if(!this.item.length){
13306             this.clearValue();
13307             return;
13308         }
13309             
13310         var value = [];
13311         var _this = this;
13312         Roo.each(this.item, function(i){
13313             if(_this.valueField){
13314                 value.push(i[_this.valueField]);
13315                 return;
13316             }
13317
13318             value.push(i);
13319         });
13320
13321         this.value = value.join(',');
13322
13323         if(this.hiddenField){
13324             this.hiddenField.dom.value = this.value;
13325         }
13326         
13327         this.store.fireEvent("datachanged", this.store);
13328     },
13329     
13330     clearItem : function()
13331     {
13332         if(!this.multiple){
13333             return;
13334         }
13335         
13336         this.item = [];
13337         
13338         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13339            c.remove();
13340         });
13341         
13342         this.syncValue();
13343         
13344         this.validate();
13345         
13346         if(this.tickable && !Roo.isTouch){
13347             this.view.refresh();
13348         }
13349     },
13350     
13351     inputEl: function ()
13352     {
13353         if(Roo.isTouch && this.mobileTouchView){
13354             return this.el.select('input.form-control',true).first();
13355         }
13356         
13357         if(this.tickable){
13358             return this.searchField;
13359         }
13360         
13361         return this.el.select('input.form-control',true).first();
13362     },
13363     
13364     
13365     onTickableFooterButtonClick : function(e, btn, el)
13366     {
13367         e.preventDefault();
13368         
13369         this.lastItem = Roo.apply([], this.item);
13370         
13371         if(btn && btn.name == 'cancel'){
13372             this.tickItems = Roo.apply([], this.item);
13373             this.collapse();
13374             return;
13375         }
13376         
13377         this.clearItem();
13378         
13379         var _this = this;
13380         
13381         Roo.each(this.tickItems, function(o){
13382             _this.addItem(o);
13383         });
13384         
13385         this.collapse();
13386         
13387     },
13388     
13389     validate : function()
13390     {
13391         var v = this.getRawValue();
13392         
13393         if(this.multiple){
13394             v = this.getValue();
13395         }
13396         
13397         if(this.disabled || this.allowBlank || v.length){
13398             this.markValid();
13399             return true;
13400         }
13401         
13402         this.markInvalid();
13403         return false;
13404     },
13405     
13406     tickableInputEl : function()
13407     {
13408         if(!this.tickable || !this.editable){
13409             return this.inputEl();
13410         }
13411         
13412         return this.inputEl().select('.select2-search-field-input', true).first();
13413     },
13414     
13415     
13416     getAutoCreateTouchView : function()
13417     {
13418         var id = Roo.id();
13419         
13420         var cfg = {
13421             cls: 'form-group' //input-group
13422         };
13423         
13424         var input =  {
13425             tag: 'input',
13426             id : id,
13427             type : this.inputType,
13428             cls : 'form-control x-combo-noedit',
13429             autocomplete: 'new-password',
13430             placeholder : this.placeholder || '',
13431             readonly : true
13432         };
13433         
13434         if (this.name) {
13435             input.name = this.name;
13436         }
13437         
13438         if (this.size) {
13439             input.cls += ' input-' + this.size;
13440         }
13441         
13442         if (this.disabled) {
13443             input.disabled = true;
13444         }
13445         
13446         var inputblock = {
13447             cls : '',
13448             cn : [
13449                 input
13450             ]
13451         };
13452         
13453         if(this.before){
13454             inputblock.cls += ' input-group';
13455             
13456             inputblock.cn.unshift({
13457                 tag :'span',
13458                 cls : 'input-group-addon',
13459                 html : this.before
13460             });
13461         }
13462         
13463         if(this.removable && !this.multiple){
13464             inputblock.cls += ' roo-removable';
13465             
13466             inputblock.cn.push({
13467                 tag: 'button',
13468                 html : 'x',
13469                 cls : 'roo-combo-removable-btn close'
13470             });
13471         }
13472
13473         if(this.hasFeedback && !this.allowBlank){
13474             
13475             inputblock.cls += ' has-feedback';
13476             
13477             inputblock.cn.push({
13478                 tag: 'span',
13479                 cls: 'glyphicon form-control-feedback'
13480             });
13481             
13482         }
13483         
13484         if (this.after) {
13485             
13486             inputblock.cls += (this.before) ? '' : ' input-group';
13487             
13488             inputblock.cn.push({
13489                 tag :'span',
13490                 cls : 'input-group-addon',
13491                 html : this.after
13492             });
13493         }
13494
13495         var box = {
13496             tag: 'div',
13497             cn: [
13498                 {
13499                     tag: 'input',
13500                     type : 'hidden',
13501                     cls: 'form-hidden-field'
13502                 },
13503                 inputblock
13504             ]
13505             
13506         };
13507         
13508         if(this.multiple){
13509             box = {
13510                 tag: 'div',
13511                 cn: [
13512                     {
13513                         tag: 'input',
13514                         type : 'hidden',
13515                         cls: 'form-hidden-field'
13516                     },
13517                     {
13518                         tag: 'ul',
13519                         cls: 'select2-choices',
13520                         cn:[
13521                             {
13522                                 tag: 'li',
13523                                 cls: 'select2-search-field',
13524                                 cn: [
13525
13526                                     inputblock
13527                                 ]
13528                             }
13529                         ]
13530                     }
13531                 ]
13532             }
13533         };
13534         
13535         var combobox = {
13536             cls: 'select2-container input-group',
13537             cn: [
13538                 box
13539             ]
13540         };
13541         
13542         if(this.multiple){
13543             combobox.cls += ' select2-container-multi';
13544         }
13545         
13546         var align = this.labelAlign || this.parentLabelAlign();
13547         
13548         cfg.cn = combobox;
13549         
13550         if(this.fieldLabel.length){
13551             
13552             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13553             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13554             
13555             cfg.cn = [
13556                 {
13557                     tag: 'label',
13558                     cls : 'control-label ' + lw,
13559                     html : this.fieldLabel
13560
13561                 },
13562                 {
13563                     cls : cw, 
13564                     cn: [
13565                         combobox
13566                     ]
13567                 }
13568             ];
13569         }
13570         
13571         var settings = this;
13572         
13573         ['xs','sm','md','lg'].map(function(size){
13574             if (settings[size]) {
13575                 cfg.cls += ' col-' + size + '-' + settings[size];
13576             }
13577         });
13578         
13579         return cfg;
13580     },
13581     
13582     initTouchView : function()
13583     {
13584         this.renderTouchView();
13585         
13586         this.touchViewEl.on('scroll', function(){
13587             this.el.dom.scrollTop = 0;
13588         }, this);
13589         
13590         this.originalValue = this.getValue();
13591         
13592         this.inputEl().on("click", this.showTouchView, this);
13593         
13594         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13595         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13596         
13597         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13598         
13599         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13600         this.store.on('load', this.onTouchViewLoad, this);
13601         this.store.on('loadexception', this.onTouchViewLoadException, this);
13602         
13603         if(this.hiddenName){
13604             
13605             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13606             
13607             this.hiddenField.dom.value =
13608                 this.hiddenValue !== undefined ? this.hiddenValue :
13609                 this.value !== undefined ? this.value : '';
13610         
13611             this.el.dom.removeAttribute('name');
13612             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13613         }
13614         
13615         if(this.multiple){
13616             this.choices = this.el.select('ul.select2-choices', true).first();
13617             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13618         }
13619         
13620         if(this.removable && !this.multiple){
13621             var close = this.closeTriggerEl();
13622             if(close){
13623                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13624                 close.on('click', this.removeBtnClick, this, close);
13625             }
13626         }
13627         /*
13628          * fix the bug in Safari iOS8
13629          */
13630         this.inputEl().on("focus", function(e){
13631             document.activeElement.blur();
13632         }, this);
13633         
13634         return;
13635         
13636         
13637     },
13638     
13639     renderTouchView : function()
13640     {
13641         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13642         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13643         
13644         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13645         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13646         
13647         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13648         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13649         this.touchViewBodyEl.setStyle('overflow', 'auto');
13650         
13651         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13652         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13653         
13654         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13655         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13656         
13657     },
13658     
13659     showTouchView : function()
13660     {
13661         if(this.disabled){
13662             return;
13663         }
13664         
13665         this.touchViewHeaderEl.hide();
13666
13667         if(this.fieldLabel.length){
13668             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13669             this.touchViewHeaderEl.show();
13670         }
13671
13672         this.touchViewEl.show();
13673
13674         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13675         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13676
13677         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13678
13679         if(this.fieldLabel.length){
13680             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13681         }
13682         
13683         this.touchViewBodyEl.setHeight(bodyHeight);
13684
13685         if(this.animate){
13686             var _this = this;
13687             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13688         }else{
13689             this.touchViewEl.addClass('in');
13690         }
13691
13692         this.doTouchViewQuery();
13693         
13694     },
13695     
13696     hideTouchView : function()
13697     {
13698         this.touchViewEl.removeClass('in');
13699
13700         if(this.animate){
13701             var _this = this;
13702             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13703         }else{
13704             this.touchViewEl.setStyle('display', 'none');
13705         }
13706         
13707     },
13708     
13709     setTouchViewValue : function()
13710     {
13711         if(this.multiple){
13712             this.clearItem();
13713         
13714             var _this = this;
13715
13716             Roo.each(this.tickItems, function(o){
13717                 this.addItem(o);
13718             }, this);
13719         }
13720         
13721         this.hideTouchView();
13722     },
13723     
13724     doTouchViewQuery : function()
13725     {
13726         var qe = {
13727             query: '',
13728             forceAll: true,
13729             combo: this,
13730             cancel:false
13731         };
13732         
13733         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13734             return false;
13735         }
13736         
13737         if(!this.alwaysQuery || this.mode == 'local'){
13738             this.onTouchViewLoad();
13739             return;
13740         }
13741         
13742         this.store.load();
13743     },
13744     
13745     onTouchViewBeforeLoad : function(combo,opts)
13746     {
13747         return;
13748     },
13749
13750     // private
13751     onTouchViewLoad : function()
13752     {
13753         if(this.store.getCount() < 1){
13754             this.onTouchViewEmptyResults();
13755             return;
13756         }
13757         
13758         this.clearTouchView();
13759         
13760         var rawValue = this.getRawValue();
13761         
13762         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13763         
13764         this.tickItems = [];
13765         
13766         this.store.data.each(function(d, rowIndex){
13767             var row = this.touchViewListGroup.createChild(template);
13768             
13769             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13770                 var cfg = {
13771                     data : d.data,
13772                     html : d.data[this.displayField]
13773                 };
13774                 
13775                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13776                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13777                 }
13778             }
13779             
13780             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13781                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13782             }
13783             
13784             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13785                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13786                 this.tickItems.push(d.data);
13787             }
13788             
13789             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13790             
13791         }, this);
13792         
13793         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13794         
13795         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13796
13797         if(this.fieldLabel.length){
13798             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13799         }
13800
13801         var listHeight = this.touchViewListGroup.getHeight();
13802         
13803         var _this = this;
13804         
13805         if(firstChecked && listHeight > bodyHeight){
13806             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13807         }
13808         
13809     },
13810     
13811     onTouchViewLoadException : function()
13812     {
13813         this.hideTouchView();
13814     },
13815     
13816     onTouchViewEmptyResults : function()
13817     {
13818         this.clearTouchView();
13819         
13820         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13821         
13822         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13823         
13824     },
13825     
13826     clearTouchView : function()
13827     {
13828         this.touchViewListGroup.dom.innerHTML = '';
13829     },
13830     
13831     onTouchViewClick : function(e, el, o)
13832     {
13833         e.preventDefault();
13834         
13835         var row = o.row;
13836         var rowIndex = o.rowIndex;
13837         
13838         var r = this.store.getAt(rowIndex);
13839         
13840         if(!this.multiple){
13841             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13842                 c.dom.removeAttribute('checked');
13843             }, this);
13844             
13845             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13846         
13847             this.setFromData(r.data);
13848             
13849             var close = this.closeTriggerEl();
13850         
13851             if(close){
13852                 close.show();
13853             }
13854
13855             this.hideTouchView();
13856             
13857             this.fireEvent('select', this, r, rowIndex);
13858             
13859             return;
13860         }
13861         
13862         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13863             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13864             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13865             return;
13866         }
13867         
13868         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13869         this.addItem(r.data);
13870         this.tickItems.push(r.data);
13871         
13872     }
13873     
13874
13875     /** 
13876     * @cfg {Boolean} grow 
13877     * @hide 
13878     */
13879     /** 
13880     * @cfg {Number} growMin 
13881     * @hide 
13882     */
13883     /** 
13884     * @cfg {Number} growMax 
13885     * @hide 
13886     */
13887     /**
13888      * @hide
13889      * @method autoSize
13890      */
13891 });
13892
13893 Roo.apply(Roo.bootstrap.ComboBox,  {
13894     
13895     header : {
13896         tag: 'div',
13897         cls: 'modal-header',
13898         cn: [
13899             {
13900                 tag: 'h4',
13901                 cls: 'modal-title'
13902             }
13903         ]
13904     },
13905     
13906     body : {
13907         tag: 'div',
13908         cls: 'modal-body',
13909         cn: [
13910             {
13911                 tag: 'ul',
13912                 cls: 'list-group'
13913             }
13914         ]
13915     },
13916     
13917     listItemRadio : {
13918         tag: 'li',
13919         cls: 'list-group-item',
13920         cn: [
13921             {
13922                 tag: 'span',
13923                 cls: 'roo-combobox-list-group-item-value'
13924             },
13925             {
13926                 tag: 'div',
13927                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13928                 cn: [
13929                     {
13930                         tag: 'input',
13931                         type: 'radio'
13932                     },
13933                     {
13934                         tag: 'label'
13935                     }
13936                 ]
13937             }
13938         ]
13939     },
13940     
13941     listItemCheckbox : {
13942         tag: 'li',
13943         cls: 'list-group-item',
13944         cn: [
13945             {
13946                 tag: 'span',
13947                 cls: 'roo-combobox-list-group-item-value'
13948             },
13949             {
13950                 tag: 'div',
13951                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13952                 cn: [
13953                     {
13954                         tag: 'input',
13955                         type: 'checkbox'
13956                     },
13957                     {
13958                         tag: 'label'
13959                     }
13960                 ]
13961             }
13962         ]
13963     },
13964     
13965     emptyResult : {
13966         tag: 'div',
13967         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13968     },
13969     
13970     footer : {
13971         tag: 'div',
13972         cls: 'modal-footer',
13973         cn: [
13974             {
13975                 tag: 'div',
13976                 cls: 'row',
13977                 cn: [
13978                     {
13979                         tag: 'div',
13980                         cls: 'col-xs-6 text-left',
13981                         cn: {
13982                             tag: 'button',
13983                             cls: 'btn btn-danger roo-touch-view-cancel',
13984                             html: 'Cancel'
13985                         }
13986                     },
13987                     {
13988                         tag: 'div',
13989                         cls: 'col-xs-6 text-right',
13990                         cn: {
13991                             tag: 'button',
13992                             cls: 'btn btn-success roo-touch-view-ok',
13993                             html: 'OK'
13994                         }
13995                     }
13996                 ]
13997             }
13998         ]
13999         
14000     }
14001 });
14002
14003 Roo.apply(Roo.bootstrap.ComboBox,  {
14004     
14005     touchViewTemplate : {
14006         tag: 'div',
14007         cls: 'modal fade roo-combobox-touch-view',
14008         cn: [
14009             {
14010                 tag: 'div',
14011                 cls: 'modal-dialog',
14012                 style : 'position:fixed', // we have to fix position....
14013                 cn: [
14014                     {
14015                         tag: 'div',
14016                         cls: 'modal-content',
14017                         cn: [
14018                             Roo.bootstrap.ComboBox.header,
14019                             Roo.bootstrap.ComboBox.body,
14020                             Roo.bootstrap.ComboBox.footer
14021                         ]
14022                     }
14023                 ]
14024             }
14025         ]
14026     }
14027 });/*
14028  * Based on:
14029  * Ext JS Library 1.1.1
14030  * Copyright(c) 2006-2007, Ext JS, LLC.
14031  *
14032  * Originally Released Under LGPL - original licence link has changed is not relivant.
14033  *
14034  * Fork - LGPL
14035  * <script type="text/javascript">
14036  */
14037
14038 /**
14039  * @class Roo.View
14040  * @extends Roo.util.Observable
14041  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14042  * This class also supports single and multi selection modes. <br>
14043  * Create a data model bound view:
14044  <pre><code>
14045  var store = new Roo.data.Store(...);
14046
14047  var view = new Roo.View({
14048     el : "my-element",
14049     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14050  
14051     singleSelect: true,
14052     selectedClass: "ydataview-selected",
14053     store: store
14054  });
14055
14056  // listen for node click?
14057  view.on("click", function(vw, index, node, e){
14058  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14059  });
14060
14061  // load XML data
14062  dataModel.load("foobar.xml");
14063  </code></pre>
14064  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14065  * <br><br>
14066  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14067  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14068  * 
14069  * Note: old style constructor is still suported (container, template, config)
14070  * 
14071  * @constructor
14072  * Create a new View
14073  * @param {Object} config The config object
14074  * 
14075  */
14076 Roo.View = function(config, depreciated_tpl, depreciated_config){
14077     
14078     this.parent = false;
14079     
14080     if (typeof(depreciated_tpl) == 'undefined') {
14081         // new way.. - universal constructor.
14082         Roo.apply(this, config);
14083         this.el  = Roo.get(this.el);
14084     } else {
14085         // old format..
14086         this.el  = Roo.get(config);
14087         this.tpl = depreciated_tpl;
14088         Roo.apply(this, depreciated_config);
14089     }
14090     this.wrapEl  = this.el.wrap().wrap();
14091     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14092     
14093     
14094     if(typeof(this.tpl) == "string"){
14095         this.tpl = new Roo.Template(this.tpl);
14096     } else {
14097         // support xtype ctors..
14098         this.tpl = new Roo.factory(this.tpl, Roo);
14099     }
14100     
14101     
14102     this.tpl.compile();
14103     
14104     /** @private */
14105     this.addEvents({
14106         /**
14107          * @event beforeclick
14108          * Fires before a click is processed. Returns false to cancel the default action.
14109          * @param {Roo.View} this
14110          * @param {Number} index The index of the target node
14111          * @param {HTMLElement} node The target node
14112          * @param {Roo.EventObject} e The raw event object
14113          */
14114             "beforeclick" : true,
14115         /**
14116          * @event click
14117          * Fires when a template node is clicked.
14118          * @param {Roo.View} this
14119          * @param {Number} index The index of the target node
14120          * @param {HTMLElement} node The target node
14121          * @param {Roo.EventObject} e The raw event object
14122          */
14123             "click" : true,
14124         /**
14125          * @event dblclick
14126          * Fires when a template node is double clicked.
14127          * @param {Roo.View} this
14128          * @param {Number} index The index of the target node
14129          * @param {HTMLElement} node The target node
14130          * @param {Roo.EventObject} e The raw event object
14131          */
14132             "dblclick" : true,
14133         /**
14134          * @event contextmenu
14135          * Fires when a template node is right clicked.
14136          * @param {Roo.View} this
14137          * @param {Number} index The index of the target node
14138          * @param {HTMLElement} node The target node
14139          * @param {Roo.EventObject} e The raw event object
14140          */
14141             "contextmenu" : true,
14142         /**
14143          * @event selectionchange
14144          * Fires when the selected nodes change.
14145          * @param {Roo.View} this
14146          * @param {Array} selections Array of the selected nodes
14147          */
14148             "selectionchange" : true,
14149     
14150         /**
14151          * @event beforeselect
14152          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14153          * @param {Roo.View} this
14154          * @param {HTMLElement} node The node to be selected
14155          * @param {Array} selections Array of currently selected nodes
14156          */
14157             "beforeselect" : true,
14158         /**
14159          * @event preparedata
14160          * Fires on every row to render, to allow you to change the data.
14161          * @param {Roo.View} this
14162          * @param {Object} data to be rendered (change this)
14163          */
14164           "preparedata" : true
14165           
14166           
14167         });
14168
14169
14170
14171     this.el.on({
14172         "click": this.onClick,
14173         "dblclick": this.onDblClick,
14174         "contextmenu": this.onContextMenu,
14175         scope:this
14176     });
14177
14178     this.selections = [];
14179     this.nodes = [];
14180     this.cmp = new Roo.CompositeElementLite([]);
14181     if(this.store){
14182         this.store = Roo.factory(this.store, Roo.data);
14183         this.setStore(this.store, true);
14184     }
14185     
14186     if ( this.footer && this.footer.xtype) {
14187            
14188          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14189         
14190         this.footer.dataSource = this.store;
14191         this.footer.container = fctr;
14192         this.footer = Roo.factory(this.footer, Roo);
14193         fctr.insertFirst(this.el);
14194         
14195         // this is a bit insane - as the paging toolbar seems to detach the el..
14196 //        dom.parentNode.parentNode.parentNode
14197          // they get detached?
14198     }
14199     
14200     
14201     Roo.View.superclass.constructor.call(this);
14202     
14203     
14204 };
14205
14206 Roo.extend(Roo.View, Roo.util.Observable, {
14207     
14208      /**
14209      * @cfg {Roo.data.Store} store Data store to load data from.
14210      */
14211     store : false,
14212     
14213     /**
14214      * @cfg {String|Roo.Element} el The container element.
14215      */
14216     el : '',
14217     
14218     /**
14219      * @cfg {String|Roo.Template} tpl The template used by this View 
14220      */
14221     tpl : false,
14222     /**
14223      * @cfg {String} dataName the named area of the template to use as the data area
14224      *                          Works with domtemplates roo-name="name"
14225      */
14226     dataName: false,
14227     /**
14228      * @cfg {String} selectedClass The css class to add to selected nodes
14229      */
14230     selectedClass : "x-view-selected",
14231      /**
14232      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14233      */
14234     emptyText : "",
14235     
14236     /**
14237      * @cfg {String} text to display on mask (default Loading)
14238      */
14239     mask : false,
14240     /**
14241      * @cfg {Boolean} multiSelect Allow multiple selection
14242      */
14243     multiSelect : false,
14244     /**
14245      * @cfg {Boolean} singleSelect Allow single selection
14246      */
14247     singleSelect:  false,
14248     
14249     /**
14250      * @cfg {Boolean} toggleSelect - selecting 
14251      */
14252     toggleSelect : false,
14253     
14254     /**
14255      * @cfg {Boolean} tickable - selecting 
14256      */
14257     tickable : false,
14258     
14259     /**
14260      * Returns the element this view is bound to.
14261      * @return {Roo.Element}
14262      */
14263     getEl : function(){
14264         return this.wrapEl;
14265     },
14266     
14267     
14268
14269     /**
14270      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14271      */
14272     refresh : function(){
14273         //Roo.log('refresh');
14274         var t = this.tpl;
14275         
14276         // if we are using something like 'domtemplate', then
14277         // the what gets used is:
14278         // t.applySubtemplate(NAME, data, wrapping data..)
14279         // the outer template then get' applied with
14280         //     the store 'extra data'
14281         // and the body get's added to the
14282         //      roo-name="data" node?
14283         //      <span class='roo-tpl-{name}'></span> ?????
14284         
14285         
14286         
14287         this.clearSelections();
14288         this.el.update("");
14289         var html = [];
14290         var records = this.store.getRange();
14291         if(records.length < 1) {
14292             
14293             // is this valid??  = should it render a template??
14294             
14295             this.el.update(this.emptyText);
14296             return;
14297         }
14298         var el = this.el;
14299         if (this.dataName) {
14300             this.el.update(t.apply(this.store.meta)); //????
14301             el = this.el.child('.roo-tpl-' + this.dataName);
14302         }
14303         
14304         for(var i = 0, len = records.length; i < len; i++){
14305             var data = this.prepareData(records[i].data, i, records[i]);
14306             this.fireEvent("preparedata", this, data, i, records[i]);
14307             
14308             var d = Roo.apply({}, data);
14309             
14310             if(this.tickable){
14311                 Roo.apply(d, {'roo-id' : Roo.id()});
14312                 
14313                 var _this = this;
14314             
14315                 Roo.each(this.parent.item, function(item){
14316                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14317                         return;
14318                     }
14319                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14320                 });
14321             }
14322             
14323             html[html.length] = Roo.util.Format.trim(
14324                 this.dataName ?
14325                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14326                     t.apply(d)
14327             );
14328         }
14329         
14330         
14331         
14332         el.update(html.join(""));
14333         this.nodes = el.dom.childNodes;
14334         this.updateIndexes(0);
14335     },
14336     
14337
14338     /**
14339      * Function to override to reformat the data that is sent to
14340      * the template for each node.
14341      * DEPRICATED - use the preparedata event handler.
14342      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14343      * a JSON object for an UpdateManager bound view).
14344      */
14345     prepareData : function(data, index, record)
14346     {
14347         this.fireEvent("preparedata", this, data, index, record);
14348         return data;
14349     },
14350
14351     onUpdate : function(ds, record){
14352         // Roo.log('on update');   
14353         this.clearSelections();
14354         var index = this.store.indexOf(record);
14355         var n = this.nodes[index];
14356         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14357         n.parentNode.removeChild(n);
14358         this.updateIndexes(index, index);
14359     },
14360
14361     
14362     
14363 // --------- FIXME     
14364     onAdd : function(ds, records, index)
14365     {
14366         //Roo.log(['on Add', ds, records, index] );        
14367         this.clearSelections();
14368         if(this.nodes.length == 0){
14369             this.refresh();
14370             return;
14371         }
14372         var n = this.nodes[index];
14373         for(var i = 0, len = records.length; i < len; i++){
14374             var d = this.prepareData(records[i].data, i, records[i]);
14375             if(n){
14376                 this.tpl.insertBefore(n, d);
14377             }else{
14378                 
14379                 this.tpl.append(this.el, d);
14380             }
14381         }
14382         this.updateIndexes(index);
14383     },
14384
14385     onRemove : function(ds, record, index){
14386        // Roo.log('onRemove');
14387         this.clearSelections();
14388         var el = this.dataName  ?
14389             this.el.child('.roo-tpl-' + this.dataName) :
14390             this.el; 
14391         
14392         el.dom.removeChild(this.nodes[index]);
14393         this.updateIndexes(index);
14394     },
14395
14396     /**
14397      * Refresh an individual node.
14398      * @param {Number} index
14399      */
14400     refreshNode : function(index){
14401         this.onUpdate(this.store, this.store.getAt(index));
14402     },
14403
14404     updateIndexes : function(startIndex, endIndex){
14405         var ns = this.nodes;
14406         startIndex = startIndex || 0;
14407         endIndex = endIndex || ns.length - 1;
14408         for(var i = startIndex; i <= endIndex; i++){
14409             ns[i].nodeIndex = i;
14410         }
14411     },
14412
14413     /**
14414      * Changes the data store this view uses and refresh the view.
14415      * @param {Store} store
14416      */
14417     setStore : function(store, initial){
14418         if(!initial && this.store){
14419             this.store.un("datachanged", this.refresh);
14420             this.store.un("add", this.onAdd);
14421             this.store.un("remove", this.onRemove);
14422             this.store.un("update", this.onUpdate);
14423             this.store.un("clear", this.refresh);
14424             this.store.un("beforeload", this.onBeforeLoad);
14425             this.store.un("load", this.onLoad);
14426             this.store.un("loadexception", this.onLoad);
14427         }
14428         if(store){
14429           
14430             store.on("datachanged", this.refresh, this);
14431             store.on("add", this.onAdd, this);
14432             store.on("remove", this.onRemove, this);
14433             store.on("update", this.onUpdate, this);
14434             store.on("clear", this.refresh, this);
14435             store.on("beforeload", this.onBeforeLoad, this);
14436             store.on("load", this.onLoad, this);
14437             store.on("loadexception", this.onLoad, this);
14438         }
14439         
14440         if(store){
14441             this.refresh();
14442         }
14443     },
14444     /**
14445      * onbeforeLoad - masks the loading area.
14446      *
14447      */
14448     onBeforeLoad : function(store,opts)
14449     {
14450          //Roo.log('onBeforeLoad');   
14451         if (!opts.add) {
14452             this.el.update("");
14453         }
14454         this.el.mask(this.mask ? this.mask : "Loading" ); 
14455     },
14456     onLoad : function ()
14457     {
14458         this.el.unmask();
14459     },
14460     
14461
14462     /**
14463      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14464      * @param {HTMLElement} node
14465      * @return {HTMLElement} The template node
14466      */
14467     findItemFromChild : function(node){
14468         var el = this.dataName  ?
14469             this.el.child('.roo-tpl-' + this.dataName,true) :
14470             this.el.dom; 
14471         
14472         if(!node || node.parentNode == el){
14473                     return node;
14474             }
14475             var p = node.parentNode;
14476             while(p && p != el){
14477             if(p.parentNode == el){
14478                 return p;
14479             }
14480             p = p.parentNode;
14481         }
14482             return null;
14483     },
14484
14485     /** @ignore */
14486     onClick : function(e){
14487         var item = this.findItemFromChild(e.getTarget());
14488         if(item){
14489             var index = this.indexOf(item);
14490             if(this.onItemClick(item, index, e) !== false){
14491                 this.fireEvent("click", this, index, item, e);
14492             }
14493         }else{
14494             this.clearSelections();
14495         }
14496     },
14497
14498     /** @ignore */
14499     onContextMenu : function(e){
14500         var item = this.findItemFromChild(e.getTarget());
14501         if(item){
14502             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14503         }
14504     },
14505
14506     /** @ignore */
14507     onDblClick : function(e){
14508         var item = this.findItemFromChild(e.getTarget());
14509         if(item){
14510             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14511         }
14512     },
14513
14514     onItemClick : function(item, index, e)
14515     {
14516         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14517             return false;
14518         }
14519         if (this.toggleSelect) {
14520             var m = this.isSelected(item) ? 'unselect' : 'select';
14521             //Roo.log(m);
14522             var _t = this;
14523             _t[m](item, true, false);
14524             return true;
14525         }
14526         if(this.multiSelect || this.singleSelect){
14527             if(this.multiSelect && e.shiftKey && this.lastSelection){
14528                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14529             }else{
14530                 this.select(item, this.multiSelect && e.ctrlKey);
14531                 this.lastSelection = item;
14532             }
14533             
14534             if(!this.tickable){
14535                 e.preventDefault();
14536             }
14537             
14538         }
14539         return true;
14540     },
14541
14542     /**
14543      * Get the number of selected nodes.
14544      * @return {Number}
14545      */
14546     getSelectionCount : function(){
14547         return this.selections.length;
14548     },
14549
14550     /**
14551      * Get the currently selected nodes.
14552      * @return {Array} An array of HTMLElements
14553      */
14554     getSelectedNodes : function(){
14555         return this.selections;
14556     },
14557
14558     /**
14559      * Get the indexes of the selected nodes.
14560      * @return {Array}
14561      */
14562     getSelectedIndexes : function(){
14563         var indexes = [], s = this.selections;
14564         for(var i = 0, len = s.length; i < len; i++){
14565             indexes.push(s[i].nodeIndex);
14566         }
14567         return indexes;
14568     },
14569
14570     /**
14571      * Clear all selections
14572      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14573      */
14574     clearSelections : function(suppressEvent){
14575         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14576             this.cmp.elements = this.selections;
14577             this.cmp.removeClass(this.selectedClass);
14578             this.selections = [];
14579             if(!suppressEvent){
14580                 this.fireEvent("selectionchange", this, this.selections);
14581             }
14582         }
14583     },
14584
14585     /**
14586      * Returns true if the passed node is selected
14587      * @param {HTMLElement/Number} node The node or node index
14588      * @return {Boolean}
14589      */
14590     isSelected : function(node){
14591         var s = this.selections;
14592         if(s.length < 1){
14593             return false;
14594         }
14595         node = this.getNode(node);
14596         return s.indexOf(node) !== -1;
14597     },
14598
14599     /**
14600      * Selects nodes.
14601      * @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
14602      * @param {Boolean} keepExisting (optional) true to keep existing selections
14603      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14604      */
14605     select : function(nodeInfo, keepExisting, suppressEvent){
14606         if(nodeInfo instanceof Array){
14607             if(!keepExisting){
14608                 this.clearSelections(true);
14609             }
14610             for(var i = 0, len = nodeInfo.length; i < len; i++){
14611                 this.select(nodeInfo[i], true, true);
14612             }
14613             return;
14614         } 
14615         var node = this.getNode(nodeInfo);
14616         if(!node || this.isSelected(node)){
14617             return; // already selected.
14618         }
14619         if(!keepExisting){
14620             this.clearSelections(true);
14621         }
14622         
14623         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14624             Roo.fly(node).addClass(this.selectedClass);
14625             this.selections.push(node);
14626             if(!suppressEvent){
14627                 this.fireEvent("selectionchange", this, this.selections);
14628             }
14629         }
14630         
14631         
14632     },
14633       /**
14634      * Unselects nodes.
14635      * @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
14636      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14637      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14638      */
14639     unselect : function(nodeInfo, keepExisting, suppressEvent)
14640     {
14641         if(nodeInfo instanceof Array){
14642             Roo.each(this.selections, function(s) {
14643                 this.unselect(s, nodeInfo);
14644             }, this);
14645             return;
14646         }
14647         var node = this.getNode(nodeInfo);
14648         if(!node || !this.isSelected(node)){
14649             //Roo.log("not selected");
14650             return; // not selected.
14651         }
14652         // fireevent???
14653         var ns = [];
14654         Roo.each(this.selections, function(s) {
14655             if (s == node ) {
14656                 Roo.fly(node).removeClass(this.selectedClass);
14657
14658                 return;
14659             }
14660             ns.push(s);
14661         },this);
14662         
14663         this.selections= ns;
14664         this.fireEvent("selectionchange", this, this.selections);
14665     },
14666
14667     /**
14668      * Gets a template node.
14669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14670      * @return {HTMLElement} The node or null if it wasn't found
14671      */
14672     getNode : function(nodeInfo){
14673         if(typeof nodeInfo == "string"){
14674             return document.getElementById(nodeInfo);
14675         }else if(typeof nodeInfo == "number"){
14676             return this.nodes[nodeInfo];
14677         }
14678         return nodeInfo;
14679     },
14680
14681     /**
14682      * Gets a range template nodes.
14683      * @param {Number} startIndex
14684      * @param {Number} endIndex
14685      * @return {Array} An array of nodes
14686      */
14687     getNodes : function(start, end){
14688         var ns = this.nodes;
14689         start = start || 0;
14690         end = typeof end == "undefined" ? ns.length - 1 : end;
14691         var nodes = [];
14692         if(start <= end){
14693             for(var i = start; i <= end; i++){
14694                 nodes.push(ns[i]);
14695             }
14696         } else{
14697             for(var i = start; i >= end; i--){
14698                 nodes.push(ns[i]);
14699             }
14700         }
14701         return nodes;
14702     },
14703
14704     /**
14705      * Finds the index of the passed node
14706      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14707      * @return {Number} The index of the node or -1
14708      */
14709     indexOf : function(node){
14710         node = this.getNode(node);
14711         if(typeof node.nodeIndex == "number"){
14712             return node.nodeIndex;
14713         }
14714         var ns = this.nodes;
14715         for(var i = 0, len = ns.length; i < len; i++){
14716             if(ns[i] == node){
14717                 return i;
14718             }
14719         }
14720         return -1;
14721     }
14722 });
14723 /*
14724  * - LGPL
14725  *
14726  * based on jquery fullcalendar
14727  * 
14728  */
14729
14730 Roo.bootstrap = Roo.bootstrap || {};
14731 /**
14732  * @class Roo.bootstrap.Calendar
14733  * @extends Roo.bootstrap.Component
14734  * Bootstrap Calendar class
14735  * @cfg {Boolean} loadMask (true|false) default false
14736  * @cfg {Object} header generate the user specific header of the calendar, default false
14737
14738  * @constructor
14739  * Create a new Container
14740  * @param {Object} config The config object
14741  */
14742
14743
14744
14745 Roo.bootstrap.Calendar = function(config){
14746     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14747      this.addEvents({
14748         /**
14749              * @event select
14750              * Fires when a date is selected
14751              * @param {DatePicker} this
14752              * @param {Date} date The selected date
14753              */
14754         'select': true,
14755         /**
14756              * @event monthchange
14757              * Fires when the displayed month changes 
14758              * @param {DatePicker} this
14759              * @param {Date} date The selected month
14760              */
14761         'monthchange': true,
14762         /**
14763              * @event evententer
14764              * Fires when mouse over an event
14765              * @param {Calendar} this
14766              * @param {event} Event
14767              */
14768         'evententer': true,
14769         /**
14770              * @event eventleave
14771              * Fires when the mouse leaves an
14772              * @param {Calendar} this
14773              * @param {event}
14774              */
14775         'eventleave': true,
14776         /**
14777              * @event eventclick
14778              * Fires when the mouse click an
14779              * @param {Calendar} this
14780              * @param {event}
14781              */
14782         'eventclick': true
14783         
14784     });
14785
14786 };
14787
14788 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14789     
14790      /**
14791      * @cfg {Number} startDay
14792      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14793      */
14794     startDay : 0,
14795     
14796     loadMask : false,
14797     
14798     header : false,
14799       
14800     getAutoCreate : function(){
14801         
14802         
14803         var fc_button = function(name, corner, style, content ) {
14804             return Roo.apply({},{
14805                 tag : 'span',
14806                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14807                          (corner.length ?
14808                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14809                             ''
14810                         ),
14811                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14812                 unselectable: 'on'
14813             });
14814         };
14815         
14816         var header = {};
14817         
14818         if(!this.header){
14819             header = {
14820                 tag : 'table',
14821                 cls : 'fc-header',
14822                 style : 'width:100%',
14823                 cn : [
14824                     {
14825                         tag: 'tr',
14826                         cn : [
14827                             {
14828                                 tag : 'td',
14829                                 cls : 'fc-header-left',
14830                                 cn : [
14831                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14832                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14833                                     { tag: 'span', cls: 'fc-header-space' },
14834                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14835
14836
14837                                 ]
14838                             },
14839
14840                             {
14841                                 tag : 'td',
14842                                 cls : 'fc-header-center',
14843                                 cn : [
14844                                     {
14845                                         tag: 'span',
14846                                         cls: 'fc-header-title',
14847                                         cn : {
14848                                             tag: 'H2',
14849                                             html : 'month / year'
14850                                         }
14851                                     }
14852
14853                                 ]
14854                             },
14855                             {
14856                                 tag : 'td',
14857                                 cls : 'fc-header-right',
14858                                 cn : [
14859                               /*      fc_button('month', 'left', '', 'month' ),
14860                                     fc_button('week', '', '', 'week' ),
14861                                     fc_button('day', 'right', '', 'day' )
14862                                 */    
14863
14864                                 ]
14865                             }
14866
14867                         ]
14868                     }
14869                 ]
14870             };
14871         }
14872         
14873         header = this.header;
14874         
14875        
14876         var cal_heads = function() {
14877             var ret = [];
14878             // fixme - handle this.
14879             
14880             for (var i =0; i < Date.dayNames.length; i++) {
14881                 var d = Date.dayNames[i];
14882                 ret.push({
14883                     tag: 'th',
14884                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14885                     html : d.substring(0,3)
14886                 });
14887                 
14888             }
14889             ret[0].cls += ' fc-first';
14890             ret[6].cls += ' fc-last';
14891             return ret;
14892         };
14893         var cal_cell = function(n) {
14894             return  {
14895                 tag: 'td',
14896                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14897                 cn : [
14898                     {
14899                         cn : [
14900                             {
14901                                 cls: 'fc-day-number',
14902                                 html: 'D'
14903                             },
14904                             {
14905                                 cls: 'fc-day-content',
14906                              
14907                                 cn : [
14908                                      {
14909                                         style: 'position: relative;' // height: 17px;
14910                                     }
14911                                 ]
14912                             }
14913                             
14914                             
14915                         ]
14916                     }
14917                 ]
14918                 
14919             }
14920         };
14921         var cal_rows = function() {
14922             
14923             var ret = [];
14924             for (var r = 0; r < 6; r++) {
14925                 var row= {
14926                     tag : 'tr',
14927                     cls : 'fc-week',
14928                     cn : []
14929                 };
14930                 
14931                 for (var i =0; i < Date.dayNames.length; i++) {
14932                     var d = Date.dayNames[i];
14933                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14934
14935                 }
14936                 row.cn[0].cls+=' fc-first';
14937                 row.cn[0].cn[0].style = 'min-height:90px';
14938                 row.cn[6].cls+=' fc-last';
14939                 ret.push(row);
14940                 
14941             }
14942             ret[0].cls += ' fc-first';
14943             ret[4].cls += ' fc-prev-last';
14944             ret[5].cls += ' fc-last';
14945             return ret;
14946             
14947         };
14948         
14949         var cal_table = {
14950             tag: 'table',
14951             cls: 'fc-border-separate',
14952             style : 'width:100%',
14953             cellspacing  : 0,
14954             cn : [
14955                 { 
14956                     tag: 'thead',
14957                     cn : [
14958                         { 
14959                             tag: 'tr',
14960                             cls : 'fc-first fc-last',
14961                             cn : cal_heads()
14962                         }
14963                     ]
14964                 },
14965                 { 
14966                     tag: 'tbody',
14967                     cn : cal_rows()
14968                 }
14969                   
14970             ]
14971         };
14972          
14973          var cfg = {
14974             cls : 'fc fc-ltr',
14975             cn : [
14976                 header,
14977                 {
14978                     cls : 'fc-content',
14979                     style : "position: relative;",
14980                     cn : [
14981                         {
14982                             cls : 'fc-view fc-view-month fc-grid',
14983                             style : 'position: relative',
14984                             unselectable : 'on',
14985                             cn : [
14986                                 {
14987                                     cls : 'fc-event-container',
14988                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14989                                 },
14990                                 cal_table
14991                             ]
14992                         }
14993                     ]
14994     
14995                 }
14996            ] 
14997             
14998         };
14999         
15000          
15001         
15002         return cfg;
15003     },
15004     
15005     
15006     initEvents : function()
15007     {
15008         if(!this.store){
15009             throw "can not find store for calendar";
15010         }
15011         
15012         var mark = {
15013             tag: "div",
15014             cls:"x-dlg-mask",
15015             style: "text-align:center",
15016             cn: [
15017                 {
15018                     tag: "div",
15019                     style: "background-color:white;width:50%;margin:250 auto",
15020                     cn: [
15021                         {
15022                             tag: "img",
15023                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15024                         },
15025                         {
15026                             tag: "span",
15027                             html: "Loading"
15028                         }
15029                         
15030                     ]
15031                 }
15032             ]
15033         };
15034         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15035         
15036         var size = this.el.select('.fc-content', true).first().getSize();
15037         this.maskEl.setSize(size.width, size.height);
15038         this.maskEl.enableDisplayMode("block");
15039         if(!this.loadMask){
15040             this.maskEl.hide();
15041         }
15042         
15043         this.store = Roo.factory(this.store, Roo.data);
15044         this.store.on('load', this.onLoad, this);
15045         this.store.on('beforeload', this.onBeforeLoad, this);
15046         
15047         this.resize();
15048         
15049         this.cells = this.el.select('.fc-day',true);
15050         //Roo.log(this.cells);
15051         this.textNodes = this.el.query('.fc-day-number');
15052         this.cells.addClassOnOver('fc-state-hover');
15053         
15054         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15055         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15056         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15057         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15058         
15059         this.on('monthchange', this.onMonthChange, this);
15060         
15061         this.update(new Date().clearTime());
15062     },
15063     
15064     resize : function() {
15065         var sz  = this.el.getSize();
15066         
15067         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15068         this.el.select('.fc-day-content div',true).setHeight(34);
15069     },
15070     
15071     
15072     // private
15073     showPrevMonth : function(e){
15074         this.update(this.activeDate.add("mo", -1));
15075     },
15076     showToday : function(e){
15077         this.update(new Date().clearTime());
15078     },
15079     // private
15080     showNextMonth : function(e){
15081         this.update(this.activeDate.add("mo", 1));
15082     },
15083
15084     // private
15085     showPrevYear : function(){
15086         this.update(this.activeDate.add("y", -1));
15087     },
15088
15089     // private
15090     showNextYear : function(){
15091         this.update(this.activeDate.add("y", 1));
15092     },
15093
15094     
15095    // private
15096     update : function(date)
15097     {
15098         var vd = this.activeDate;
15099         this.activeDate = date;
15100 //        if(vd && this.el){
15101 //            var t = date.getTime();
15102 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15103 //                Roo.log('using add remove');
15104 //                
15105 //                this.fireEvent('monthchange', this, date);
15106 //                
15107 //                this.cells.removeClass("fc-state-highlight");
15108 //                this.cells.each(function(c){
15109 //                   if(c.dateValue == t){
15110 //                       c.addClass("fc-state-highlight");
15111 //                       setTimeout(function(){
15112 //                            try{c.dom.firstChild.focus();}catch(e){}
15113 //                       }, 50);
15114 //                       return false;
15115 //                   }
15116 //                   return true;
15117 //                });
15118 //                return;
15119 //            }
15120 //        }
15121         
15122         var days = date.getDaysInMonth();
15123         
15124         var firstOfMonth = date.getFirstDateOfMonth();
15125         var startingPos = firstOfMonth.getDay()-this.startDay;
15126         
15127         if(startingPos < this.startDay){
15128             startingPos += 7;
15129         }
15130         
15131         var pm = date.add(Date.MONTH, -1);
15132         var prevStart = pm.getDaysInMonth()-startingPos;
15133 //        
15134         this.cells = this.el.select('.fc-day',true);
15135         this.textNodes = this.el.query('.fc-day-number');
15136         this.cells.addClassOnOver('fc-state-hover');
15137         
15138         var cells = this.cells.elements;
15139         var textEls = this.textNodes;
15140         
15141         Roo.each(cells, function(cell){
15142             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15143         });
15144         
15145         days += startingPos;
15146
15147         // convert everything to numbers so it's fast
15148         var day = 86400000;
15149         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15150         //Roo.log(d);
15151         //Roo.log(pm);
15152         //Roo.log(prevStart);
15153         
15154         var today = new Date().clearTime().getTime();
15155         var sel = date.clearTime().getTime();
15156         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15157         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15158         var ddMatch = this.disabledDatesRE;
15159         var ddText = this.disabledDatesText;
15160         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15161         var ddaysText = this.disabledDaysText;
15162         var format = this.format;
15163         
15164         var setCellClass = function(cal, cell){
15165             cell.row = 0;
15166             cell.events = [];
15167             cell.more = [];
15168             //Roo.log('set Cell Class');
15169             cell.title = "";
15170             var t = d.getTime();
15171             
15172             //Roo.log(d);
15173             
15174             cell.dateValue = t;
15175             if(t == today){
15176                 cell.className += " fc-today";
15177                 cell.className += " fc-state-highlight";
15178                 cell.title = cal.todayText;
15179             }
15180             if(t == sel){
15181                 // disable highlight in other month..
15182                 //cell.className += " fc-state-highlight";
15183                 
15184             }
15185             // disabling
15186             if(t < min) {
15187                 cell.className = " fc-state-disabled";
15188                 cell.title = cal.minText;
15189                 return;
15190             }
15191             if(t > max) {
15192                 cell.className = " fc-state-disabled";
15193                 cell.title = cal.maxText;
15194                 return;
15195             }
15196             if(ddays){
15197                 if(ddays.indexOf(d.getDay()) != -1){
15198                     cell.title = ddaysText;
15199                     cell.className = " fc-state-disabled";
15200                 }
15201             }
15202             if(ddMatch && format){
15203                 var fvalue = d.dateFormat(format);
15204                 if(ddMatch.test(fvalue)){
15205                     cell.title = ddText.replace("%0", fvalue);
15206                     cell.className = " fc-state-disabled";
15207                 }
15208             }
15209             
15210             if (!cell.initialClassName) {
15211                 cell.initialClassName = cell.dom.className;
15212             }
15213             
15214             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15215         };
15216
15217         var i = 0;
15218         
15219         for(; i < startingPos; i++) {
15220             textEls[i].innerHTML = (++prevStart);
15221             d.setDate(d.getDate()+1);
15222             
15223             cells[i].className = "fc-past fc-other-month";
15224             setCellClass(this, cells[i]);
15225         }
15226         
15227         var intDay = 0;
15228         
15229         for(; i < days; i++){
15230             intDay = i - startingPos + 1;
15231             textEls[i].innerHTML = (intDay);
15232             d.setDate(d.getDate()+1);
15233             
15234             cells[i].className = ''; // "x-date-active";
15235             setCellClass(this, cells[i]);
15236         }
15237         var extraDays = 0;
15238         
15239         for(; i < 42; i++) {
15240             textEls[i].innerHTML = (++extraDays);
15241             d.setDate(d.getDate()+1);
15242             
15243             cells[i].className = "fc-future fc-other-month";
15244             setCellClass(this, cells[i]);
15245         }
15246         
15247         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15248         
15249         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15250         
15251         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15252         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15253         
15254         if(totalRows != 6){
15255             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15256             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15257         }
15258         
15259         this.fireEvent('monthchange', this, date);
15260         
15261         
15262         /*
15263         if(!this.internalRender){
15264             var main = this.el.dom.firstChild;
15265             var w = main.offsetWidth;
15266             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15267             Roo.fly(main).setWidth(w);
15268             this.internalRender = true;
15269             // opera does not respect the auto grow header center column
15270             // then, after it gets a width opera refuses to recalculate
15271             // without a second pass
15272             if(Roo.isOpera && !this.secondPass){
15273                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15274                 this.secondPass = true;
15275                 this.update.defer(10, this, [date]);
15276             }
15277         }
15278         */
15279         
15280     },
15281     
15282     findCell : function(dt) {
15283         dt = dt.clearTime().getTime();
15284         var ret = false;
15285         this.cells.each(function(c){
15286             //Roo.log("check " +c.dateValue + '?=' + dt);
15287             if(c.dateValue == dt){
15288                 ret = c;
15289                 return false;
15290             }
15291             return true;
15292         });
15293         
15294         return ret;
15295     },
15296     
15297     findCells : function(ev) {
15298         var s = ev.start.clone().clearTime().getTime();
15299        // Roo.log(s);
15300         var e= ev.end.clone().clearTime().getTime();
15301        // Roo.log(e);
15302         var ret = [];
15303         this.cells.each(function(c){
15304              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15305             
15306             if(c.dateValue > e){
15307                 return ;
15308             }
15309             if(c.dateValue < s){
15310                 return ;
15311             }
15312             ret.push(c);
15313         });
15314         
15315         return ret;    
15316     },
15317     
15318 //    findBestRow: function(cells)
15319 //    {
15320 //        var ret = 0;
15321 //        
15322 //        for (var i =0 ; i < cells.length;i++) {
15323 //            ret  = Math.max(cells[i].rows || 0,ret);
15324 //        }
15325 //        return ret;
15326 //        
15327 //    },
15328     
15329     
15330     addItem : function(ev)
15331     {
15332         // look for vertical location slot in
15333         var cells = this.findCells(ev);
15334         
15335 //        ev.row = this.findBestRow(cells);
15336         
15337         // work out the location.
15338         
15339         var crow = false;
15340         var rows = [];
15341         for(var i =0; i < cells.length; i++) {
15342             
15343             cells[i].row = cells[0].row;
15344             
15345             if(i == 0){
15346                 cells[i].row = cells[i].row + 1;
15347             }
15348             
15349             if (!crow) {
15350                 crow = {
15351                     start : cells[i],
15352                     end :  cells[i]
15353                 };
15354                 continue;
15355             }
15356             if (crow.start.getY() == cells[i].getY()) {
15357                 // on same row.
15358                 crow.end = cells[i];
15359                 continue;
15360             }
15361             // different row.
15362             rows.push(crow);
15363             crow = {
15364                 start: cells[i],
15365                 end : cells[i]
15366             };
15367             
15368         }
15369         
15370         rows.push(crow);
15371         ev.els = [];
15372         ev.rows = rows;
15373         ev.cells = cells;
15374         
15375         cells[0].events.push(ev);
15376         
15377         this.calevents.push(ev);
15378     },
15379     
15380     clearEvents: function() {
15381         
15382         if(!this.calevents){
15383             return;
15384         }
15385         
15386         Roo.each(this.cells.elements, function(c){
15387             c.row = 0;
15388             c.events = [];
15389             c.more = [];
15390         });
15391         
15392         Roo.each(this.calevents, function(e) {
15393             Roo.each(e.els, function(el) {
15394                 el.un('mouseenter' ,this.onEventEnter, this);
15395                 el.un('mouseleave' ,this.onEventLeave, this);
15396                 el.remove();
15397             },this);
15398         },this);
15399         
15400         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15401             e.remove();
15402         });
15403         
15404     },
15405     
15406     renderEvents: function()
15407     {   
15408         var _this = this;
15409         
15410         this.cells.each(function(c) {
15411             
15412             if(c.row < 5){
15413                 return;
15414             }
15415             
15416             var ev = c.events;
15417             
15418             var r = 4;
15419             if(c.row != c.events.length){
15420                 r = 4 - (4 - (c.row - c.events.length));
15421             }
15422             
15423             c.events = ev.slice(0, r);
15424             c.more = ev.slice(r);
15425             
15426             if(c.more.length && c.more.length == 1){
15427                 c.events.push(c.more.pop());
15428             }
15429             
15430             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15431             
15432         });
15433             
15434         this.cells.each(function(c) {
15435             
15436             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15437             
15438             
15439             for (var e = 0; e < c.events.length; e++){
15440                 var ev = c.events[e];
15441                 var rows = ev.rows;
15442                 
15443                 for(var i = 0; i < rows.length; i++) {
15444                 
15445                     // how many rows should it span..
15446
15447                     var  cfg = {
15448                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15449                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15450
15451                         unselectable : "on",
15452                         cn : [
15453                             {
15454                                 cls: 'fc-event-inner',
15455                                 cn : [
15456     //                                {
15457     //                                  tag:'span',
15458     //                                  cls: 'fc-event-time',
15459     //                                  html : cells.length > 1 ? '' : ev.time
15460     //                                },
15461                                     {
15462                                       tag:'span',
15463                                       cls: 'fc-event-title',
15464                                       html : String.format('{0}', ev.title)
15465                                     }
15466
15467
15468                                 ]
15469                             },
15470                             {
15471                                 cls: 'ui-resizable-handle ui-resizable-e',
15472                                 html : '&nbsp;&nbsp;&nbsp'
15473                             }
15474
15475                         ]
15476                     };
15477
15478                     if (i == 0) {
15479                         cfg.cls += ' fc-event-start';
15480                     }
15481                     if ((i+1) == rows.length) {
15482                         cfg.cls += ' fc-event-end';
15483                     }
15484
15485                     var ctr = _this.el.select('.fc-event-container',true).first();
15486                     var cg = ctr.createChild(cfg);
15487
15488                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15489                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15490
15491                     var r = (c.more.length) ? 1 : 0;
15492                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15493                     cg.setWidth(ebox.right - sbox.x -2);
15494
15495                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15496                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15497                     cg.on('click', _this.onEventClick, _this, ev);
15498
15499                     ev.els.push(cg);
15500                     
15501                 }
15502                 
15503             }
15504             
15505             
15506             if(c.more.length){
15507                 var  cfg = {
15508                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15509                     style : 'position: absolute',
15510                     unselectable : "on",
15511                     cn : [
15512                         {
15513                             cls: 'fc-event-inner',
15514                             cn : [
15515                                 {
15516                                   tag:'span',
15517                                   cls: 'fc-event-title',
15518                                   html : 'More'
15519                                 }
15520
15521
15522                             ]
15523                         },
15524                         {
15525                             cls: 'ui-resizable-handle ui-resizable-e',
15526                             html : '&nbsp;&nbsp;&nbsp'
15527                         }
15528
15529                     ]
15530                 };
15531
15532                 var ctr = _this.el.select('.fc-event-container',true).first();
15533                 var cg = ctr.createChild(cfg);
15534
15535                 var sbox = c.select('.fc-day-content',true).first().getBox();
15536                 var ebox = c.select('.fc-day-content',true).first().getBox();
15537                 //Roo.log(cg);
15538                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15539                 cg.setWidth(ebox.right - sbox.x -2);
15540
15541                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15542                 
15543             }
15544             
15545         });
15546         
15547         
15548         
15549     },
15550     
15551     onEventEnter: function (e, el,event,d) {
15552         this.fireEvent('evententer', this, el, event);
15553     },
15554     
15555     onEventLeave: function (e, el,event,d) {
15556         this.fireEvent('eventleave', this, el, event);
15557     },
15558     
15559     onEventClick: function (e, el,event,d) {
15560         this.fireEvent('eventclick', this, el, event);
15561     },
15562     
15563     onMonthChange: function () {
15564         this.store.load();
15565     },
15566     
15567     onMoreEventClick: function(e, el, more)
15568     {
15569         var _this = this;
15570         
15571         this.calpopover.placement = 'right';
15572         this.calpopover.setTitle('More');
15573         
15574         this.calpopover.setContent('');
15575         
15576         var ctr = this.calpopover.el.select('.popover-content', true).first();
15577         
15578         Roo.each(more, function(m){
15579             var cfg = {
15580                 cls : 'fc-event-hori fc-event-draggable',
15581                 html : m.title
15582             };
15583             var cg = ctr.createChild(cfg);
15584             
15585             cg.on('click', _this.onEventClick, _this, m);
15586         });
15587         
15588         this.calpopover.show(el);
15589         
15590         
15591     },
15592     
15593     onLoad: function () 
15594     {   
15595         this.calevents = [];
15596         var cal = this;
15597         
15598         if(this.store.getCount() > 0){
15599             this.store.data.each(function(d){
15600                cal.addItem({
15601                     id : d.data.id,
15602                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15603                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15604                     time : d.data.start_time,
15605                     title : d.data.title,
15606                     description : d.data.description,
15607                     venue : d.data.venue
15608                 });
15609             });
15610         }
15611         
15612         this.renderEvents();
15613         
15614         if(this.calevents.length && this.loadMask){
15615             this.maskEl.hide();
15616         }
15617     },
15618     
15619     onBeforeLoad: function()
15620     {
15621         this.clearEvents();
15622         if(this.loadMask){
15623             this.maskEl.show();
15624         }
15625     }
15626 });
15627
15628  
15629  /*
15630  * - LGPL
15631  *
15632  * element
15633  * 
15634  */
15635
15636 /**
15637  * @class Roo.bootstrap.Popover
15638  * @extends Roo.bootstrap.Component
15639  * Bootstrap Popover class
15640  * @cfg {String} html contents of the popover   (or false to use children..)
15641  * @cfg {String} title of popover (or false to hide)
15642  * @cfg {String} placement how it is placed
15643  * @cfg {String} trigger click || hover (or false to trigger manually)
15644  * @cfg {String} over what (parent or false to trigger manually.)
15645  * @cfg {Number} delay - delay before showing
15646  
15647  * @constructor
15648  * Create a new Popover
15649  * @param {Object} config The config object
15650  */
15651
15652 Roo.bootstrap.Popover = function(config){
15653     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15654     
15655     this.addEvents({
15656         // raw events
15657          /**
15658          * @event show
15659          * After the popover show
15660          * 
15661          * @param {Roo.bootstrap.Popover} this
15662          */
15663         "show" : true,
15664         /**
15665          * @event hide
15666          * After the popover hide
15667          * 
15668          * @param {Roo.bootstrap.Popover} this
15669          */
15670         "hide" : true
15671     });
15672 };
15673
15674 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15675     
15676     title: 'Fill in a title',
15677     html: false,
15678     
15679     placement : 'right',
15680     trigger : 'hover', // hover
15681     
15682     delay : 0,
15683     
15684     over: 'parent',
15685     
15686     can_build_overlaid : false,
15687     
15688     getChildContainer : function()
15689     {
15690         return this.el.select('.popover-content',true).first();
15691     },
15692     
15693     getAutoCreate : function(){
15694          
15695         var cfg = {
15696            cls : 'popover roo-dynamic',
15697            style: 'display:block',
15698            cn : [
15699                 {
15700                     cls : 'arrow'
15701                 },
15702                 {
15703                     cls : 'popover-inner',
15704                     cn : [
15705                         {
15706                             tag: 'h3',
15707                             cls: 'popover-title',
15708                             html : this.title
15709                         },
15710                         {
15711                             cls : 'popover-content',
15712                             html : this.html
15713                         }
15714                     ]
15715                     
15716                 }
15717            ]
15718         };
15719         
15720         return cfg;
15721     },
15722     setTitle: function(str)
15723     {
15724         this.title = str;
15725         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15726     },
15727     setContent: function(str)
15728     {
15729         this.html = str;
15730         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15731     },
15732     // as it get's added to the bottom of the page.
15733     onRender : function(ct, position)
15734     {
15735         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15736         if(!this.el){
15737             var cfg = Roo.apply({},  this.getAutoCreate());
15738             cfg.id = Roo.id();
15739             
15740             if (this.cls) {
15741                 cfg.cls += ' ' + this.cls;
15742             }
15743             if (this.style) {
15744                 cfg.style = this.style;
15745             }
15746             //Roo.log("adding to ");
15747             this.el = Roo.get(document.body).createChild(cfg, position);
15748 //            Roo.log(this.el);
15749         }
15750         this.initEvents();
15751     },
15752     
15753     initEvents : function()
15754     {
15755         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15756         this.el.enableDisplayMode('block');
15757         this.el.hide();
15758         if (this.over === false) {
15759             return; 
15760         }
15761         if (this.triggers === false) {
15762             return;
15763         }
15764         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15765         var triggers = this.trigger ? this.trigger.split(' ') : [];
15766         Roo.each(triggers, function(trigger) {
15767         
15768             if (trigger == 'click') {
15769                 on_el.on('click', this.toggle, this);
15770             } else if (trigger != 'manual') {
15771                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15772                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15773       
15774                 on_el.on(eventIn  ,this.enter, this);
15775                 on_el.on(eventOut, this.leave, this);
15776             }
15777         }, this);
15778         
15779     },
15780     
15781     
15782     // private
15783     timeout : null,
15784     hoverState : null,
15785     
15786     toggle : function () {
15787         this.hoverState == 'in' ? this.leave() : this.enter();
15788     },
15789     
15790     enter : function () {
15791        
15792     
15793         clearTimeout(this.timeout);
15794     
15795         this.hoverState = 'in';
15796     
15797         if (!this.delay || !this.delay.show) {
15798             this.show();
15799             return;
15800         }
15801         var _t = this;
15802         this.timeout = setTimeout(function () {
15803             if (_t.hoverState == 'in') {
15804                 _t.show();
15805             }
15806         }, this.delay.show)
15807     },
15808     leave : function() {
15809         clearTimeout(this.timeout);
15810     
15811         this.hoverState = 'out';
15812     
15813         if (!this.delay || !this.delay.hide) {
15814             this.hide();
15815             return;
15816         }
15817         var _t = this;
15818         this.timeout = setTimeout(function () {
15819             if (_t.hoverState == 'out') {
15820                 _t.hide();
15821             }
15822         }, this.delay.hide)
15823     },
15824     
15825     show : function (on_el)
15826     {
15827         if (!on_el) {
15828             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15829         }
15830         // set content.
15831         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15832         if (this.html !== false) {
15833             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15834         }
15835         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15836         if (!this.title.length) {
15837             this.el.select('.popover-title',true).hide();
15838         }
15839         
15840         var placement = typeof this.placement == 'function' ?
15841             this.placement.call(this, this.el, on_el) :
15842             this.placement;
15843             
15844         var autoToken = /\s?auto?\s?/i;
15845         var autoPlace = autoToken.test(placement);
15846         if (autoPlace) {
15847             placement = placement.replace(autoToken, '') || 'top';
15848         }
15849         
15850         //this.el.detach()
15851         //this.el.setXY([0,0]);
15852         this.el.show();
15853         this.el.dom.style.display='block';
15854         this.el.addClass(placement);
15855         
15856         //this.el.appendTo(on_el);
15857         
15858         var p = this.getPosition();
15859         var box = this.el.getBox();
15860         
15861         if (autoPlace) {
15862             // fixme..
15863         }
15864         var align = Roo.bootstrap.Popover.alignment[placement];
15865         this.el.alignTo(on_el, align[0],align[1]);
15866         //var arrow = this.el.select('.arrow',true).first();
15867         //arrow.set(align[2], 
15868         
15869         this.el.addClass('in');
15870         
15871         
15872         if (this.el.hasClass('fade')) {
15873             // fade it?
15874         }
15875         
15876         this.fireEvent('show', this);
15877         
15878     },
15879     hide : function()
15880     {
15881         this.el.setXY([0,0]);
15882         this.el.removeClass('in');
15883         this.el.hide();
15884         this.hoverState = null;
15885         
15886         this.fireEvent('hide', this);
15887     }
15888     
15889 });
15890
15891 Roo.bootstrap.Popover.alignment = {
15892     'left' : ['r-l', [-10,0], 'right'],
15893     'right' : ['l-r', [10,0], 'left'],
15894     'bottom' : ['t-b', [0,10], 'top'],
15895     'top' : [ 'b-t', [0,-10], 'bottom']
15896 };
15897
15898  /*
15899  * - LGPL
15900  *
15901  * Progress
15902  * 
15903  */
15904
15905 /**
15906  * @class Roo.bootstrap.Progress
15907  * @extends Roo.bootstrap.Component
15908  * Bootstrap Progress class
15909  * @cfg {Boolean} striped striped of the progress bar
15910  * @cfg {Boolean} active animated of the progress bar
15911  * 
15912  * 
15913  * @constructor
15914  * Create a new Progress
15915  * @param {Object} config The config object
15916  */
15917
15918 Roo.bootstrap.Progress = function(config){
15919     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15920 };
15921
15922 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15923     
15924     striped : false,
15925     active: false,
15926     
15927     getAutoCreate : function(){
15928         var cfg = {
15929             tag: 'div',
15930             cls: 'progress'
15931         };
15932         
15933         
15934         if(this.striped){
15935             cfg.cls += ' progress-striped';
15936         }
15937       
15938         if(this.active){
15939             cfg.cls += ' active';
15940         }
15941         
15942         
15943         return cfg;
15944     }
15945    
15946 });
15947
15948  
15949
15950  /*
15951  * - LGPL
15952  *
15953  * ProgressBar
15954  * 
15955  */
15956
15957 /**
15958  * @class Roo.bootstrap.ProgressBar
15959  * @extends Roo.bootstrap.Component
15960  * Bootstrap ProgressBar class
15961  * @cfg {Number} aria_valuenow aria-value now
15962  * @cfg {Number} aria_valuemin aria-value min
15963  * @cfg {Number} aria_valuemax aria-value max
15964  * @cfg {String} label label for the progress bar
15965  * @cfg {String} panel (success | info | warning | danger )
15966  * @cfg {String} role role of the progress bar
15967  * @cfg {String} sr_only text
15968  * 
15969  * 
15970  * @constructor
15971  * Create a new ProgressBar
15972  * @param {Object} config The config object
15973  */
15974
15975 Roo.bootstrap.ProgressBar = function(config){
15976     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15977 };
15978
15979 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15980     
15981     aria_valuenow : 0,
15982     aria_valuemin : 0,
15983     aria_valuemax : 100,
15984     label : false,
15985     panel : false,
15986     role : false,
15987     sr_only: false,
15988     
15989     getAutoCreate : function()
15990     {
15991         
15992         var cfg = {
15993             tag: 'div',
15994             cls: 'progress-bar',
15995             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15996         };
15997         
15998         if(this.sr_only){
15999             cfg.cn = {
16000                 tag: 'span',
16001                 cls: 'sr-only',
16002                 html: this.sr_only
16003             }
16004         }
16005         
16006         if(this.role){
16007             cfg.role = this.role;
16008         }
16009         
16010         if(this.aria_valuenow){
16011             cfg['aria-valuenow'] = this.aria_valuenow;
16012         }
16013         
16014         if(this.aria_valuemin){
16015             cfg['aria-valuemin'] = this.aria_valuemin;
16016         }
16017         
16018         if(this.aria_valuemax){
16019             cfg['aria-valuemax'] = this.aria_valuemax;
16020         }
16021         
16022         if(this.label && !this.sr_only){
16023             cfg.html = this.label;
16024         }
16025         
16026         if(this.panel){
16027             cfg.cls += ' progress-bar-' + this.panel;
16028         }
16029         
16030         return cfg;
16031     },
16032     
16033     update : function(aria_valuenow)
16034     {
16035         this.aria_valuenow = aria_valuenow;
16036         
16037         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16038     }
16039    
16040 });
16041
16042  
16043
16044  /*
16045  * - LGPL
16046  *
16047  * column
16048  * 
16049  */
16050
16051 /**
16052  * @class Roo.bootstrap.TabGroup
16053  * @extends Roo.bootstrap.Column
16054  * Bootstrap Column class
16055  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16056  * @cfg {Boolean} carousel true to make the group behave like a carousel
16057  * @cfg {Boolean} bullets show bullets for the panels
16058  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16059  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16060  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16061  * 
16062  * @constructor
16063  * Create a new TabGroup
16064  * @param {Object} config The config object
16065  */
16066
16067 Roo.bootstrap.TabGroup = function(config){
16068     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16069     if (!this.navId) {
16070         this.navId = Roo.id();
16071     }
16072     this.tabs = [];
16073     Roo.bootstrap.TabGroup.register(this);
16074     
16075 };
16076
16077 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16078     
16079     carousel : false,
16080     transition : false,
16081     bullets : 0,
16082     timer : 0,
16083     autoslide : false,
16084     slideFn : false,
16085     slideOnTouch : false,
16086     
16087     getAutoCreate : function()
16088     {
16089         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16090         
16091         cfg.cls += ' tab-content';
16092         
16093         if (this.carousel) {
16094             cfg.cls += ' carousel slide';
16095             
16096             cfg.cn = [{
16097                cls : 'carousel-inner'
16098             }];
16099         
16100             if(this.bullets  && !Roo.isTouch){
16101                 
16102                 var bullets = {
16103                     cls : 'carousel-bullets',
16104                     cn : []
16105                 };
16106                
16107                 if(this.bullets_cls){
16108                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16109                 }
16110                  /*
16111                 for (var i = 0; i < this.bullets; i++){
16112                     bullets.cn.push({
16113                         cls : 'bullet bullet-' + i
16114                     });
16115                 }
16116                 */
16117                 bullets.cn.push({
16118                     cls : 'clear'
16119                 });
16120                 
16121                 cfg.cn[0].cn = bullets;
16122             }
16123         }
16124         
16125         return cfg;
16126     },
16127     
16128     initEvents:  function()
16129     {
16130         if(Roo.isTouch && this.slideOnTouch){
16131             this.el.on("touchstart", this.onTouchStart, this);
16132         }
16133         
16134         if(this.autoslide){
16135             var _this = this;
16136             
16137             this.slideFn = window.setInterval(function() {
16138                 _this.showPanelNext();
16139             }, this.timer);
16140         }
16141         
16142     },
16143     
16144     onTouchStart : function(e, el, o)
16145     {
16146         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16147             return;
16148         }
16149         
16150         this.showPanelNext();
16151     },
16152     
16153     getChildContainer : function()
16154     {
16155         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16156     },
16157     
16158     /**
16159     * register a Navigation item
16160     * @param {Roo.bootstrap.NavItem} the navitem to add
16161     */
16162     register : function(item)
16163     {
16164         this.tabs.push( item);
16165         item.navId = this.navId; // not really needed..
16166         this.addBullet();
16167     
16168     },
16169     
16170     getActivePanel : function()
16171     {
16172         var r = false;
16173         Roo.each(this.tabs, function(t) {
16174             if (t.active) {
16175                 r = t;
16176                 return false;
16177             }
16178             return null;
16179         });
16180         return r;
16181         
16182     },
16183     getPanelByName : function(n)
16184     {
16185         var r = false;
16186         Roo.each(this.tabs, function(t) {
16187             if (t.tabId == n) {
16188                 r = t;
16189                 return false;
16190             }
16191             return null;
16192         });
16193         return r;
16194     },
16195     indexOfPanel : function(p)
16196     {
16197         var r = false;
16198         Roo.each(this.tabs, function(t,i) {
16199             if (t.tabId == p.tabId) {
16200                 r = i;
16201                 return false;
16202             }
16203             return null;
16204         });
16205         return r;
16206     },
16207     /**
16208      * show a specific panel
16209      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16210      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16211      */
16212     showPanel : function (pan)
16213     {
16214         if(this.transition || typeof(pan) == 'undefined'){
16215             Roo.log("waiting for the transitionend");
16216             return;
16217         }
16218         
16219         if (typeof(pan) == 'number') {
16220             pan = this.tabs[pan];
16221         }
16222         
16223         if (typeof(pan) == 'string') {
16224             pan = this.getPanelByName(pan);
16225         }
16226         
16227         var cur = this.getActivePanel();
16228         
16229         if(!pan || !cur){
16230             Roo.log('pan or acitve pan is undefined');
16231             return false;
16232         }
16233         
16234         if (pan.tabId == this.getActivePanel().tabId) {
16235             return true;
16236         }
16237         
16238         if (false === cur.fireEvent('beforedeactivate')) {
16239             return false;
16240         }
16241         
16242         if(this.bullets > 0 && !Roo.isTouch){
16243             this.setActiveBullet(this.indexOfPanel(pan));
16244         }
16245         
16246         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16247             
16248             this.transition = true;
16249             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16250             var lr = dir == 'next' ? 'left' : 'right';
16251             pan.el.addClass(dir); // or prev
16252             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16253             cur.el.addClass(lr); // or right
16254             pan.el.addClass(lr);
16255             
16256             var _this = this;
16257             cur.el.on('transitionend', function() {
16258                 Roo.log("trans end?");
16259                 
16260                 pan.el.removeClass([lr,dir]);
16261                 pan.setActive(true);
16262                 
16263                 cur.el.removeClass([lr]);
16264                 cur.setActive(false);
16265                 
16266                 _this.transition = false;
16267                 
16268             }, this, { single:  true } );
16269             
16270             return true;
16271         }
16272         
16273         cur.setActive(false);
16274         pan.setActive(true);
16275         
16276         return true;
16277         
16278     },
16279     showPanelNext : function()
16280     {
16281         var i = this.indexOfPanel(this.getActivePanel());
16282         
16283         if (i >= this.tabs.length - 1 && !this.autoslide) {
16284             return;
16285         }
16286         
16287         if (i >= this.tabs.length - 1 && this.autoslide) {
16288             i = -1;
16289         }
16290         
16291         this.showPanel(this.tabs[i+1]);
16292     },
16293     
16294     showPanelPrev : function()
16295     {
16296         var i = this.indexOfPanel(this.getActivePanel());
16297         
16298         if (i  < 1 && !this.autoslide) {
16299             return;
16300         }
16301         
16302         if (i < 1 && this.autoslide) {
16303             i = this.tabs.length;
16304         }
16305         
16306         this.showPanel(this.tabs[i-1]);
16307     },
16308     
16309     
16310     addBullet: function()
16311     {
16312         if(!this.bullets || Roo.isTouch){
16313             return;
16314         }
16315         var ctr = this.el.select('.carousel-bullets',true).first();
16316         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16317         var bullet = ctr.createChild({
16318             cls : 'bullet bullet-' + i
16319         },ctr.dom.lastChild);
16320         
16321         
16322         var _this = this;
16323         
16324         bullet.on('click', (function(e, el, o, ii, t){
16325
16326             e.preventDefault();
16327
16328             this.showPanel(ii);
16329
16330             if(this.autoslide && this.slideFn){
16331                 clearInterval(this.slideFn);
16332                 this.slideFn = window.setInterval(function() {
16333                     _this.showPanelNext();
16334                 }, this.timer);
16335             }
16336
16337         }).createDelegate(this, [i, bullet], true));
16338                 
16339         
16340     },
16341      
16342     setActiveBullet : function(i)
16343     {
16344         if(Roo.isTouch){
16345             return;
16346         }
16347         
16348         Roo.each(this.el.select('.bullet', true).elements, function(el){
16349             el.removeClass('selected');
16350         });
16351
16352         var bullet = this.el.select('.bullet-' + i, true).first();
16353         
16354         if(!bullet){
16355             return;
16356         }
16357         
16358         bullet.addClass('selected');
16359     }
16360     
16361     
16362   
16363 });
16364
16365  
16366
16367  
16368  
16369 Roo.apply(Roo.bootstrap.TabGroup, {
16370     
16371     groups: {},
16372      /**
16373     * register a Navigation Group
16374     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16375     */
16376     register : function(navgrp)
16377     {
16378         this.groups[navgrp.navId] = navgrp;
16379         
16380     },
16381     /**
16382     * fetch a Navigation Group based on the navigation ID
16383     * if one does not exist , it will get created.
16384     * @param {string} the navgroup to add
16385     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16386     */
16387     get: function(navId) {
16388         if (typeof(this.groups[navId]) == 'undefined') {
16389             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16390         }
16391         return this.groups[navId] ;
16392     }
16393     
16394     
16395     
16396 });
16397
16398  /*
16399  * - LGPL
16400  *
16401  * TabPanel
16402  * 
16403  */
16404
16405 /**
16406  * @class Roo.bootstrap.TabPanel
16407  * @extends Roo.bootstrap.Component
16408  * Bootstrap TabPanel class
16409  * @cfg {Boolean} active panel active
16410  * @cfg {String} html panel content
16411  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16412  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16413  * 
16414  * 
16415  * @constructor
16416  * Create a new TabPanel
16417  * @param {Object} config The config object
16418  */
16419
16420 Roo.bootstrap.TabPanel = function(config){
16421     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16422     this.addEvents({
16423         /**
16424              * @event changed
16425              * Fires when the active status changes
16426              * @param {Roo.bootstrap.TabPanel} this
16427              * @param {Boolean} state the new state
16428             
16429          */
16430         'changed': true,
16431         /**
16432              * @event beforedeactivate
16433              * Fires before a tab is de-activated - can be used to do validation on a form.
16434              * @param {Roo.bootstrap.TabPanel} this
16435              * @return {Boolean} false if there is an error
16436             
16437          */
16438         'beforedeactivate': true
16439      });
16440     
16441     this.tabId = this.tabId || Roo.id();
16442   
16443 };
16444
16445 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16446     
16447     active: false,
16448     html: false,
16449     tabId: false,
16450     navId : false,
16451     
16452     getAutoCreate : function(){
16453         var cfg = {
16454             tag: 'div',
16455             // item is needed for carousel - not sure if it has any effect otherwise
16456             cls: 'tab-pane item',
16457             html: this.html || ''
16458         };
16459         
16460         if(this.active){
16461             cfg.cls += ' active';
16462         }
16463         
16464         if(this.tabId){
16465             cfg.tabId = this.tabId;
16466         }
16467         
16468         
16469         return cfg;
16470     },
16471     
16472     initEvents:  function()
16473     {
16474         var p = this.parent();
16475         this.navId = this.navId || p.navId;
16476         
16477         if (typeof(this.navId) != 'undefined') {
16478             // not really needed.. but just in case.. parent should be a NavGroup.
16479             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16480             
16481             tg.register(this);
16482             
16483             var i = tg.tabs.length - 1;
16484             
16485             if(this.active && tg.bullets > 0 && i < tg.bullets){
16486                 tg.setActiveBullet(i);
16487             }
16488         }
16489         
16490     },
16491     
16492     
16493     onRender : function(ct, position)
16494     {
16495        // Roo.log("Call onRender: " + this.xtype);
16496         
16497         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16498         
16499         
16500         
16501         
16502         
16503     },
16504     
16505     setActive: function(state)
16506     {
16507         Roo.log("panel - set active " + this.tabId + "=" + state);
16508         
16509         this.active = state;
16510         if (!state) {
16511             this.el.removeClass('active');
16512             
16513         } else  if (!this.el.hasClass('active')) {
16514             this.el.addClass('active');
16515         }
16516         
16517         this.fireEvent('changed', this, state);
16518     }
16519     
16520     
16521 });
16522  
16523
16524  
16525
16526  /*
16527  * - LGPL
16528  *
16529  * DateField
16530  * 
16531  */
16532
16533 /**
16534  * @class Roo.bootstrap.DateField
16535  * @extends Roo.bootstrap.Input
16536  * Bootstrap DateField class
16537  * @cfg {Number} weekStart default 0
16538  * @cfg {String} viewMode default empty, (months|years)
16539  * @cfg {String} minViewMode default empty, (months|years)
16540  * @cfg {Number} startDate default -Infinity
16541  * @cfg {Number} endDate default Infinity
16542  * @cfg {Boolean} todayHighlight default false
16543  * @cfg {Boolean} todayBtn default false
16544  * @cfg {Boolean} calendarWeeks default false
16545  * @cfg {Object} daysOfWeekDisabled default empty
16546  * @cfg {Boolean} singleMode default false (true | false)
16547  * 
16548  * @cfg {Boolean} keyboardNavigation default true
16549  * @cfg {String} language default en
16550  * 
16551  * @constructor
16552  * Create a new DateField
16553  * @param {Object} config The config object
16554  */
16555
16556 Roo.bootstrap.DateField = function(config){
16557     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16558      this.addEvents({
16559             /**
16560              * @event show
16561              * Fires when this field show.
16562              * @param {Roo.bootstrap.DateField} this
16563              * @param {Mixed} date The date value
16564              */
16565             show : true,
16566             /**
16567              * @event show
16568              * Fires when this field hide.
16569              * @param {Roo.bootstrap.DateField} this
16570              * @param {Mixed} date The date value
16571              */
16572             hide : true,
16573             /**
16574              * @event select
16575              * Fires when select a date.
16576              * @param {Roo.bootstrap.DateField} this
16577              * @param {Mixed} date The date value
16578              */
16579             select : true,
16580             /**
16581              * @event beforeselect
16582              * Fires when before select a date.
16583              * @param {Roo.bootstrap.DateField} this
16584              * @param {Mixed} date The date value
16585              */
16586             beforeselect : true
16587         });
16588 };
16589
16590 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16591     
16592     /**
16593      * @cfg {String} format
16594      * The default date format string which can be overriden for localization support.  The format must be
16595      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16596      */
16597     format : "m/d/y",
16598     /**
16599      * @cfg {String} altFormats
16600      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16601      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16602      */
16603     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16604     
16605     weekStart : 0,
16606     
16607     viewMode : '',
16608     
16609     minViewMode : '',
16610     
16611     todayHighlight : false,
16612     
16613     todayBtn: false,
16614     
16615     language: 'en',
16616     
16617     keyboardNavigation: true,
16618     
16619     calendarWeeks: false,
16620     
16621     startDate: -Infinity,
16622     
16623     endDate: Infinity,
16624     
16625     daysOfWeekDisabled: [],
16626     
16627     _events: [],
16628     
16629     singleMode : false,
16630     
16631     UTCDate: function()
16632     {
16633         return new Date(Date.UTC.apply(Date, arguments));
16634     },
16635     
16636     UTCToday: function()
16637     {
16638         var today = new Date();
16639         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16640     },
16641     
16642     getDate: function() {
16643             var d = this.getUTCDate();
16644             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16645     },
16646     
16647     getUTCDate: function() {
16648             return this.date;
16649     },
16650     
16651     setDate: function(d) {
16652             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16653     },
16654     
16655     setUTCDate: function(d) {
16656             this.date = d;
16657             this.setValue(this.formatDate(this.date));
16658     },
16659         
16660     onRender: function(ct, position)
16661     {
16662         
16663         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16664         
16665         this.language = this.language || 'en';
16666         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16667         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16668         
16669         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16670         this.format = this.format || 'm/d/y';
16671         this.isInline = false;
16672         this.isInput = true;
16673         this.component = this.el.select('.add-on', true).first() || false;
16674         this.component = (this.component && this.component.length === 0) ? false : this.component;
16675         this.hasInput = this.component && this.inputEL().length;
16676         
16677         if (typeof(this.minViewMode === 'string')) {
16678             switch (this.minViewMode) {
16679                 case 'months':
16680                     this.minViewMode = 1;
16681                     break;
16682                 case 'years':
16683                     this.minViewMode = 2;
16684                     break;
16685                 default:
16686                     this.minViewMode = 0;
16687                     break;
16688             }
16689         }
16690         
16691         if (typeof(this.viewMode === 'string')) {
16692             switch (this.viewMode) {
16693                 case 'months':
16694                     this.viewMode = 1;
16695                     break;
16696                 case 'years':
16697                     this.viewMode = 2;
16698                     break;
16699                 default:
16700                     this.viewMode = 0;
16701                     break;
16702             }
16703         }
16704                 
16705         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16706         
16707 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16708         
16709         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16710         
16711         this.picker().on('mousedown', this.onMousedown, this);
16712         this.picker().on('click', this.onClick, this);
16713         
16714         this.picker().addClass('datepicker-dropdown');
16715         
16716         this.startViewMode = this.viewMode;
16717         
16718         if(this.singleMode){
16719             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16720                 v.setVisibilityMode(Roo.Element.DISPLAY);
16721                 v.hide();
16722             });
16723             
16724             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16725                 v.setStyle('width', '189px');
16726             });
16727         }
16728         
16729         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16730             if(!this.calendarWeeks){
16731                 v.remove();
16732                 return;
16733             }
16734             
16735             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16736             v.attr('colspan', function(i, val){
16737                 return parseInt(val) + 1;
16738             });
16739         });
16740                         
16741         
16742         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16743         
16744         this.setStartDate(this.startDate);
16745         this.setEndDate(this.endDate);
16746         
16747         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16748         
16749         this.fillDow();
16750         this.fillMonths();
16751         this.update();
16752         this.showMode();
16753         
16754         if(this.isInline) {
16755             this.show();
16756         }
16757     },
16758     
16759     picker : function()
16760     {
16761         return this.pickerEl;
16762 //        return this.el.select('.datepicker', true).first();
16763     },
16764     
16765     fillDow: function()
16766     {
16767         var dowCnt = this.weekStart;
16768         
16769         var dow = {
16770             tag: 'tr',
16771             cn: [
16772                 
16773             ]
16774         };
16775         
16776         if(this.calendarWeeks){
16777             dow.cn.push({
16778                 tag: 'th',
16779                 cls: 'cw',
16780                 html: '&nbsp;'
16781             })
16782         }
16783         
16784         while (dowCnt < this.weekStart + 7) {
16785             dow.cn.push({
16786                 tag: 'th',
16787                 cls: 'dow',
16788                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16789             });
16790         }
16791         
16792         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16793     },
16794     
16795     fillMonths: function()
16796     {    
16797         var i = 0;
16798         var months = this.picker().select('>.datepicker-months td', true).first();
16799         
16800         months.dom.innerHTML = '';
16801         
16802         while (i < 12) {
16803             var month = {
16804                 tag: 'span',
16805                 cls: 'month',
16806                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16807             };
16808             
16809             months.createChild(month);
16810         }
16811         
16812     },
16813     
16814     update: function()
16815     {
16816         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;
16817         
16818         if (this.date < this.startDate) {
16819             this.viewDate = new Date(this.startDate);
16820         } else if (this.date > this.endDate) {
16821             this.viewDate = new Date(this.endDate);
16822         } else {
16823             this.viewDate = new Date(this.date);
16824         }
16825         
16826         this.fill();
16827     },
16828     
16829     fill: function() 
16830     {
16831         var d = new Date(this.viewDate),
16832                 year = d.getUTCFullYear(),
16833                 month = d.getUTCMonth(),
16834                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16835                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16836                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16837                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16838                 currentDate = this.date && this.date.valueOf(),
16839                 today = this.UTCToday();
16840         
16841         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16842         
16843 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16844         
16845 //        this.picker.select('>tfoot th.today').
16846 //                                              .text(dates[this.language].today)
16847 //                                              .toggle(this.todayBtn !== false);
16848     
16849         this.updateNavArrows();
16850         this.fillMonths();
16851                                                 
16852         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16853         
16854         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16855          
16856         prevMonth.setUTCDate(day);
16857         
16858         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16859         
16860         var nextMonth = new Date(prevMonth);
16861         
16862         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16863         
16864         nextMonth = nextMonth.valueOf();
16865         
16866         var fillMonths = false;
16867         
16868         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16869         
16870         while(prevMonth.valueOf() < nextMonth) {
16871             var clsName = '';
16872             
16873             if (prevMonth.getUTCDay() === this.weekStart) {
16874                 if(fillMonths){
16875                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16876                 }
16877                     
16878                 fillMonths = {
16879                     tag: 'tr',
16880                     cn: []
16881                 };
16882                 
16883                 if(this.calendarWeeks){
16884                     // ISO 8601: First week contains first thursday.
16885                     // ISO also states week starts on Monday, but we can be more abstract here.
16886                     var
16887                     // Start of current week: based on weekstart/current date
16888                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16889                     // Thursday of this week
16890                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16891                     // First Thursday of year, year from thursday
16892                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16893                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16894                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16895                     
16896                     fillMonths.cn.push({
16897                         tag: 'td',
16898                         cls: 'cw',
16899                         html: calWeek
16900                     });
16901                 }
16902             }
16903             
16904             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16905                 clsName += ' old';
16906             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16907                 clsName += ' new';
16908             }
16909             if (this.todayHighlight &&
16910                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16911                 prevMonth.getUTCMonth() == today.getMonth() &&
16912                 prevMonth.getUTCDate() == today.getDate()) {
16913                 clsName += ' today';
16914             }
16915             
16916             if (currentDate && prevMonth.valueOf() === currentDate) {
16917                 clsName += ' active';
16918             }
16919             
16920             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16921                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16922                     clsName += ' disabled';
16923             }
16924             
16925             fillMonths.cn.push({
16926                 tag: 'td',
16927                 cls: 'day ' + clsName,
16928                 html: prevMonth.getDate()
16929             });
16930             
16931             prevMonth.setDate(prevMonth.getDate()+1);
16932         }
16933           
16934         var currentYear = this.date && this.date.getUTCFullYear();
16935         var currentMonth = this.date && this.date.getUTCMonth();
16936         
16937         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16938         
16939         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16940             v.removeClass('active');
16941             
16942             if(currentYear === year && k === currentMonth){
16943                 v.addClass('active');
16944             }
16945             
16946             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16947                 v.addClass('disabled');
16948             }
16949             
16950         });
16951         
16952         
16953         year = parseInt(year/10, 10) * 10;
16954         
16955         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16956         
16957         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16958         
16959         year -= 1;
16960         for (var i = -1; i < 11; i++) {
16961             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16962                 tag: 'span',
16963                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16964                 html: year
16965             });
16966             
16967             year += 1;
16968         }
16969     },
16970     
16971     showMode: function(dir) 
16972     {
16973         if (dir) {
16974             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16975         }
16976         
16977         Roo.each(this.picker().select('>div',true).elements, function(v){
16978             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16979             v.hide();
16980         });
16981         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16982     },
16983     
16984     place: function()
16985     {
16986         if(this.isInline) {
16987             return;
16988         }
16989         
16990         this.picker().removeClass(['bottom', 'top']);
16991         
16992         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16993             /*
16994              * place to the top of element!
16995              *
16996              */
16997             
16998             this.picker().addClass('top');
16999             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17000             
17001             return;
17002         }
17003         
17004         this.picker().addClass('bottom');
17005         
17006         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17007     },
17008     
17009     parseDate : function(value)
17010     {
17011         if(!value || value instanceof Date){
17012             return value;
17013         }
17014         var v = Date.parseDate(value, this.format);
17015         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17016             v = Date.parseDate(value, 'Y-m-d');
17017         }
17018         if(!v && this.altFormats){
17019             if(!this.altFormatsArray){
17020                 this.altFormatsArray = this.altFormats.split("|");
17021             }
17022             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17023                 v = Date.parseDate(value, this.altFormatsArray[i]);
17024             }
17025         }
17026         return v;
17027     },
17028     
17029     formatDate : function(date, fmt)
17030     {   
17031         return (!date || !(date instanceof Date)) ?
17032         date : date.dateFormat(fmt || this.format);
17033     },
17034     
17035     onFocus : function()
17036     {
17037         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17038         this.show();
17039     },
17040     
17041     onBlur : function()
17042     {
17043         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17044         
17045         var d = this.inputEl().getValue();
17046         
17047         this.setValue(d);
17048                 
17049         this.hide();
17050     },
17051     
17052     show : function()
17053     {
17054         this.picker().show();
17055         this.update();
17056         this.place();
17057         
17058         this.fireEvent('show', this, this.date);
17059     },
17060     
17061     hide : function()
17062     {
17063         if(this.isInline) {
17064             return;
17065         }
17066         this.picker().hide();
17067         this.viewMode = this.startViewMode;
17068         this.showMode();
17069         
17070         this.fireEvent('hide', this, this.date);
17071         
17072     },
17073     
17074     onMousedown: function(e)
17075     {
17076         e.stopPropagation();
17077         e.preventDefault();
17078     },
17079     
17080     keyup: function(e)
17081     {
17082         Roo.bootstrap.DateField.superclass.keyup.call(this);
17083         this.update();
17084     },
17085
17086     setValue: function(v)
17087     {
17088         if(this.fireEvent('beforeselect', this, v) !== false){
17089             var d = new Date(this.parseDate(v) ).clearTime();
17090         
17091             if(isNaN(d.getTime())){
17092                 this.date = this.viewDate = '';
17093                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17094                 return;
17095             }
17096
17097             v = this.formatDate(d);
17098
17099             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17100
17101             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17102
17103             this.update();
17104
17105             this.fireEvent('select', this, this.date);
17106         }
17107     },
17108     
17109     getValue: function()
17110     {
17111         return this.formatDate(this.date);
17112     },
17113     
17114     fireKey: function(e)
17115     {
17116         if (!this.picker().isVisible()){
17117             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17118                 this.show();
17119             }
17120             return;
17121         }
17122         
17123         var dateChanged = false,
17124         dir, day, month,
17125         newDate, newViewDate;
17126         
17127         switch(e.keyCode){
17128             case 27: // escape
17129                 this.hide();
17130                 e.preventDefault();
17131                 break;
17132             case 37: // left
17133             case 39: // right
17134                 if (!this.keyboardNavigation) {
17135                     break;
17136                 }
17137                 dir = e.keyCode == 37 ? -1 : 1;
17138                 
17139                 if (e.ctrlKey){
17140                     newDate = this.moveYear(this.date, dir);
17141                     newViewDate = this.moveYear(this.viewDate, dir);
17142                 } else if (e.shiftKey){
17143                     newDate = this.moveMonth(this.date, dir);
17144                     newViewDate = this.moveMonth(this.viewDate, dir);
17145                 } else {
17146                     newDate = new Date(this.date);
17147                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17148                     newViewDate = new Date(this.viewDate);
17149                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17150                 }
17151                 if (this.dateWithinRange(newDate)){
17152                     this.date = newDate;
17153                     this.viewDate = newViewDate;
17154                     this.setValue(this.formatDate(this.date));
17155 //                    this.update();
17156                     e.preventDefault();
17157                     dateChanged = true;
17158                 }
17159                 break;
17160             case 38: // up
17161             case 40: // down
17162                 if (!this.keyboardNavigation) {
17163                     break;
17164                 }
17165                 dir = e.keyCode == 38 ? -1 : 1;
17166                 if (e.ctrlKey){
17167                     newDate = this.moveYear(this.date, dir);
17168                     newViewDate = this.moveYear(this.viewDate, dir);
17169                 } else if (e.shiftKey){
17170                     newDate = this.moveMonth(this.date, dir);
17171                     newViewDate = this.moveMonth(this.viewDate, dir);
17172                 } else {
17173                     newDate = new Date(this.date);
17174                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17175                     newViewDate = new Date(this.viewDate);
17176                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17177                 }
17178                 if (this.dateWithinRange(newDate)){
17179                     this.date = newDate;
17180                     this.viewDate = newViewDate;
17181                     this.setValue(this.formatDate(this.date));
17182 //                    this.update();
17183                     e.preventDefault();
17184                     dateChanged = true;
17185                 }
17186                 break;
17187             case 13: // enter
17188                 this.setValue(this.formatDate(this.date));
17189                 this.hide();
17190                 e.preventDefault();
17191                 break;
17192             case 9: // tab
17193                 this.setValue(this.formatDate(this.date));
17194                 this.hide();
17195                 break;
17196             case 16: // shift
17197             case 17: // ctrl
17198             case 18: // alt
17199                 break;
17200             default :
17201                 this.hide();
17202                 
17203         }
17204     },
17205     
17206     
17207     onClick: function(e) 
17208     {
17209         e.stopPropagation();
17210         e.preventDefault();
17211         
17212         var target = e.getTarget();
17213         
17214         if(target.nodeName.toLowerCase() === 'i'){
17215             target = Roo.get(target).dom.parentNode;
17216         }
17217         
17218         var nodeName = target.nodeName;
17219         var className = target.className;
17220         var html = target.innerHTML;
17221         //Roo.log(nodeName);
17222         
17223         switch(nodeName.toLowerCase()) {
17224             case 'th':
17225                 switch(className) {
17226                     case 'switch':
17227                         this.showMode(1);
17228                         break;
17229                     case 'prev':
17230                     case 'next':
17231                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17232                         switch(this.viewMode){
17233                                 case 0:
17234                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17235                                         break;
17236                                 case 1:
17237                                 case 2:
17238                                         this.viewDate = this.moveYear(this.viewDate, dir);
17239                                         break;
17240                         }
17241                         this.fill();
17242                         break;
17243                     case 'today':
17244                         var date = new Date();
17245                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17246 //                        this.fill()
17247                         this.setValue(this.formatDate(this.date));
17248                         
17249                         this.hide();
17250                         break;
17251                 }
17252                 break;
17253             case 'span':
17254                 if (className.indexOf('disabled') < 0) {
17255                     this.viewDate.setUTCDate(1);
17256                     if (className.indexOf('month') > -1) {
17257                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17258                     } else {
17259                         var year = parseInt(html, 10) || 0;
17260                         this.viewDate.setUTCFullYear(year);
17261                         
17262                     }
17263                     
17264                     if(this.singleMode){
17265                         this.setValue(this.formatDate(this.viewDate));
17266                         this.hide();
17267                         return;
17268                     }
17269                     
17270                     this.showMode(-1);
17271                     this.fill();
17272                 }
17273                 break;
17274                 
17275             case 'td':
17276                 //Roo.log(className);
17277                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17278                     var day = parseInt(html, 10) || 1;
17279                     var year = this.viewDate.getUTCFullYear(),
17280                         month = this.viewDate.getUTCMonth();
17281
17282                     if (className.indexOf('old') > -1) {
17283                         if(month === 0 ){
17284                             month = 11;
17285                             year -= 1;
17286                         }else{
17287                             month -= 1;
17288                         }
17289                     } else if (className.indexOf('new') > -1) {
17290                         if (month == 11) {
17291                             month = 0;
17292                             year += 1;
17293                         } else {
17294                             month += 1;
17295                         }
17296                     }
17297                     //Roo.log([year,month,day]);
17298                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17299                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17300 //                    this.fill();
17301                     //Roo.log(this.formatDate(this.date));
17302                     this.setValue(this.formatDate(this.date));
17303                     this.hide();
17304                 }
17305                 break;
17306         }
17307     },
17308     
17309     setStartDate: function(startDate)
17310     {
17311         this.startDate = startDate || -Infinity;
17312         if (this.startDate !== -Infinity) {
17313             this.startDate = this.parseDate(this.startDate);
17314         }
17315         this.update();
17316         this.updateNavArrows();
17317     },
17318
17319     setEndDate: function(endDate)
17320     {
17321         this.endDate = endDate || Infinity;
17322         if (this.endDate !== Infinity) {
17323             this.endDate = this.parseDate(this.endDate);
17324         }
17325         this.update();
17326         this.updateNavArrows();
17327     },
17328     
17329     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17330     {
17331         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17332         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17333             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17334         }
17335         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17336             return parseInt(d, 10);
17337         });
17338         this.update();
17339         this.updateNavArrows();
17340     },
17341     
17342     updateNavArrows: function() 
17343     {
17344         if(this.singleMode){
17345             return;
17346         }
17347         
17348         var d = new Date(this.viewDate),
17349         year = d.getUTCFullYear(),
17350         month = d.getUTCMonth();
17351         
17352         Roo.each(this.picker().select('.prev', true).elements, function(v){
17353             v.show();
17354             switch (this.viewMode) {
17355                 case 0:
17356
17357                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17358                         v.hide();
17359                     }
17360                     break;
17361                 case 1:
17362                 case 2:
17363                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17364                         v.hide();
17365                     }
17366                     break;
17367             }
17368         });
17369         
17370         Roo.each(this.picker().select('.next', true).elements, function(v){
17371             v.show();
17372             switch (this.viewMode) {
17373                 case 0:
17374
17375                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17376                         v.hide();
17377                     }
17378                     break;
17379                 case 1:
17380                 case 2:
17381                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17382                         v.hide();
17383                     }
17384                     break;
17385             }
17386         })
17387     },
17388     
17389     moveMonth: function(date, dir)
17390     {
17391         if (!dir) {
17392             return date;
17393         }
17394         var new_date = new Date(date.valueOf()),
17395         day = new_date.getUTCDate(),
17396         month = new_date.getUTCMonth(),
17397         mag = Math.abs(dir),
17398         new_month, test;
17399         dir = dir > 0 ? 1 : -1;
17400         if (mag == 1){
17401             test = dir == -1
17402             // If going back one month, make sure month is not current month
17403             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17404             ? function(){
17405                 return new_date.getUTCMonth() == month;
17406             }
17407             // If going forward one month, make sure month is as expected
17408             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17409             : function(){
17410                 return new_date.getUTCMonth() != new_month;
17411             };
17412             new_month = month + dir;
17413             new_date.setUTCMonth(new_month);
17414             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17415             if (new_month < 0 || new_month > 11) {
17416                 new_month = (new_month + 12) % 12;
17417             }
17418         } else {
17419             // For magnitudes >1, move one month at a time...
17420             for (var i=0; i<mag; i++) {
17421                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17422                 new_date = this.moveMonth(new_date, dir);
17423             }
17424             // ...then reset the day, keeping it in the new month
17425             new_month = new_date.getUTCMonth();
17426             new_date.setUTCDate(day);
17427             test = function(){
17428                 return new_month != new_date.getUTCMonth();
17429             };
17430         }
17431         // Common date-resetting loop -- if date is beyond end of month, make it
17432         // end of month
17433         while (test()){
17434             new_date.setUTCDate(--day);
17435             new_date.setUTCMonth(new_month);
17436         }
17437         return new_date;
17438     },
17439
17440     moveYear: function(date, dir)
17441     {
17442         return this.moveMonth(date, dir*12);
17443     },
17444
17445     dateWithinRange: function(date)
17446     {
17447         return date >= this.startDate && date <= this.endDate;
17448     },
17449
17450     
17451     remove: function() 
17452     {
17453         this.picker().remove();
17454     }
17455    
17456 });
17457
17458 Roo.apply(Roo.bootstrap.DateField,  {
17459     
17460     head : {
17461         tag: 'thead',
17462         cn: [
17463         {
17464             tag: 'tr',
17465             cn: [
17466             {
17467                 tag: 'th',
17468                 cls: 'prev',
17469                 html: '<i class="fa fa-arrow-left"/>'
17470             },
17471             {
17472                 tag: 'th',
17473                 cls: 'switch',
17474                 colspan: '5'
17475             },
17476             {
17477                 tag: 'th',
17478                 cls: 'next',
17479                 html: '<i class="fa fa-arrow-right"/>'
17480             }
17481
17482             ]
17483         }
17484         ]
17485     },
17486     
17487     content : {
17488         tag: 'tbody',
17489         cn: [
17490         {
17491             tag: 'tr',
17492             cn: [
17493             {
17494                 tag: 'td',
17495                 colspan: '7'
17496             }
17497             ]
17498         }
17499         ]
17500     },
17501     
17502     footer : {
17503         tag: 'tfoot',
17504         cn: [
17505         {
17506             tag: 'tr',
17507             cn: [
17508             {
17509                 tag: 'th',
17510                 colspan: '7',
17511                 cls: 'today'
17512             }
17513                     
17514             ]
17515         }
17516         ]
17517     },
17518     
17519     dates:{
17520         en: {
17521             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17522             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17523             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17524             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17525             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17526             today: "Today"
17527         }
17528     },
17529     
17530     modes: [
17531     {
17532         clsName: 'days',
17533         navFnc: 'Month',
17534         navStep: 1
17535     },
17536     {
17537         clsName: 'months',
17538         navFnc: 'FullYear',
17539         navStep: 1
17540     },
17541     {
17542         clsName: 'years',
17543         navFnc: 'FullYear',
17544         navStep: 10
17545     }]
17546 });
17547
17548 Roo.apply(Roo.bootstrap.DateField,  {
17549   
17550     template : {
17551         tag: 'div',
17552         cls: 'datepicker dropdown-menu roo-dynamic',
17553         cn: [
17554         {
17555             tag: 'div',
17556             cls: 'datepicker-days',
17557             cn: [
17558             {
17559                 tag: 'table',
17560                 cls: 'table-condensed',
17561                 cn:[
17562                 Roo.bootstrap.DateField.head,
17563                 {
17564                     tag: 'tbody'
17565                 },
17566                 Roo.bootstrap.DateField.footer
17567                 ]
17568             }
17569             ]
17570         },
17571         {
17572             tag: 'div',
17573             cls: 'datepicker-months',
17574             cn: [
17575             {
17576                 tag: 'table',
17577                 cls: 'table-condensed',
17578                 cn:[
17579                 Roo.bootstrap.DateField.head,
17580                 Roo.bootstrap.DateField.content,
17581                 Roo.bootstrap.DateField.footer
17582                 ]
17583             }
17584             ]
17585         },
17586         {
17587             tag: 'div',
17588             cls: 'datepicker-years',
17589             cn: [
17590             {
17591                 tag: 'table',
17592                 cls: 'table-condensed',
17593                 cn:[
17594                 Roo.bootstrap.DateField.head,
17595                 Roo.bootstrap.DateField.content,
17596                 Roo.bootstrap.DateField.footer
17597                 ]
17598             }
17599             ]
17600         }
17601         ]
17602     }
17603 });
17604
17605  
17606
17607  /*
17608  * - LGPL
17609  *
17610  * TimeField
17611  * 
17612  */
17613
17614 /**
17615  * @class Roo.bootstrap.TimeField
17616  * @extends Roo.bootstrap.Input
17617  * Bootstrap DateField class
17618  * 
17619  * 
17620  * @constructor
17621  * Create a new TimeField
17622  * @param {Object} config The config object
17623  */
17624
17625 Roo.bootstrap.TimeField = function(config){
17626     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17627     this.addEvents({
17628             /**
17629              * @event show
17630              * Fires when this field show.
17631              * @param {Roo.bootstrap.DateField} thisthis
17632              * @param {Mixed} date The date value
17633              */
17634             show : true,
17635             /**
17636              * @event show
17637              * Fires when this field hide.
17638              * @param {Roo.bootstrap.DateField} this
17639              * @param {Mixed} date The date value
17640              */
17641             hide : true,
17642             /**
17643              * @event select
17644              * Fires when select a date.
17645              * @param {Roo.bootstrap.DateField} this
17646              * @param {Mixed} date The date value
17647              */
17648             select : true
17649         });
17650 };
17651
17652 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17653     
17654     /**
17655      * @cfg {String} format
17656      * The default time format string which can be overriden for localization support.  The format must be
17657      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17658      */
17659     format : "H:i",
17660        
17661     onRender: function(ct, position)
17662     {
17663         
17664         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17665                 
17666         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17667         
17668         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17669         
17670         this.pop = this.picker().select('>.datepicker-time',true).first();
17671         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17672         
17673         this.picker().on('mousedown', this.onMousedown, this);
17674         this.picker().on('click', this.onClick, this);
17675         
17676         this.picker().addClass('datepicker-dropdown');
17677     
17678         this.fillTime();
17679         this.update();
17680             
17681         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17682         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17683         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17684         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17685         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17686         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17687
17688     },
17689     
17690     fireKey: function(e){
17691         if (!this.picker().isVisible()){
17692             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17693                 this.show();
17694             }
17695             return;
17696         }
17697
17698         e.preventDefault();
17699         
17700         switch(e.keyCode){
17701             case 27: // escape
17702                 this.hide();
17703                 break;
17704             case 37: // left
17705             case 39: // right
17706                 this.onTogglePeriod();
17707                 break;
17708             case 38: // up
17709                 this.onIncrementMinutes();
17710                 break;
17711             case 40: // down
17712                 this.onDecrementMinutes();
17713                 break;
17714             case 13: // enter
17715             case 9: // tab
17716                 this.setTime();
17717                 break;
17718         }
17719     },
17720     
17721     onClick: function(e) {
17722         e.stopPropagation();
17723         e.preventDefault();
17724     },
17725     
17726     picker : function()
17727     {
17728         return this.el.select('.datepicker', true).first();
17729     },
17730     
17731     fillTime: function()
17732     {    
17733         var time = this.pop.select('tbody', true).first();
17734         
17735         time.dom.innerHTML = '';
17736         
17737         time.createChild({
17738             tag: 'tr',
17739             cn: [
17740                 {
17741                     tag: 'td',
17742                     cn: [
17743                         {
17744                             tag: 'a',
17745                             href: '#',
17746                             cls: 'btn',
17747                             cn: [
17748                                 {
17749                                     tag: 'span',
17750                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17751                                 }
17752                             ]
17753                         } 
17754                     ]
17755                 },
17756                 {
17757                     tag: 'td',
17758                     cls: 'separator'
17759                 },
17760                 {
17761                     tag: 'td',
17762                     cn: [
17763                         {
17764                             tag: 'a',
17765                             href: '#',
17766                             cls: 'btn',
17767                             cn: [
17768                                 {
17769                                     tag: 'span',
17770                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17771                                 }
17772                             ]
17773                         }
17774                     ]
17775                 },
17776                 {
17777                     tag: 'td',
17778                     cls: 'separator'
17779                 }
17780             ]
17781         });
17782         
17783         time.createChild({
17784             tag: 'tr',
17785             cn: [
17786                 {
17787                     tag: 'td',
17788                     cn: [
17789                         {
17790                             tag: 'span',
17791                             cls: 'timepicker-hour',
17792                             html: '00'
17793                         }  
17794                     ]
17795                 },
17796                 {
17797                     tag: 'td',
17798                     cls: 'separator',
17799                     html: ':'
17800                 },
17801                 {
17802                     tag: 'td',
17803                     cn: [
17804                         {
17805                             tag: 'span',
17806                             cls: 'timepicker-minute',
17807                             html: '00'
17808                         }  
17809                     ]
17810                 },
17811                 {
17812                     tag: 'td',
17813                     cls: 'separator'
17814                 },
17815                 {
17816                     tag: 'td',
17817                     cn: [
17818                         {
17819                             tag: 'button',
17820                             type: 'button',
17821                             cls: 'btn btn-primary period',
17822                             html: 'AM'
17823                             
17824                         }
17825                     ]
17826                 }
17827             ]
17828         });
17829         
17830         time.createChild({
17831             tag: 'tr',
17832             cn: [
17833                 {
17834                     tag: 'td',
17835                     cn: [
17836                         {
17837                             tag: 'a',
17838                             href: '#',
17839                             cls: 'btn',
17840                             cn: [
17841                                 {
17842                                     tag: 'span',
17843                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17844                                 }
17845                             ]
17846                         }
17847                     ]
17848                 },
17849                 {
17850                     tag: 'td',
17851                     cls: 'separator'
17852                 },
17853                 {
17854                     tag: 'td',
17855                     cn: [
17856                         {
17857                             tag: 'a',
17858                             href: '#',
17859                             cls: 'btn',
17860                             cn: [
17861                                 {
17862                                     tag: 'span',
17863                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17864                                 }
17865                             ]
17866                         }
17867                     ]
17868                 },
17869                 {
17870                     tag: 'td',
17871                     cls: 'separator'
17872                 }
17873             ]
17874         });
17875         
17876     },
17877     
17878     update: function()
17879     {
17880         
17881         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17882         
17883         this.fill();
17884     },
17885     
17886     fill: function() 
17887     {
17888         var hours = this.time.getHours();
17889         var minutes = this.time.getMinutes();
17890         var period = 'AM';
17891         
17892         if(hours > 11){
17893             period = 'PM';
17894         }
17895         
17896         if(hours == 0){
17897             hours = 12;
17898         }
17899         
17900         
17901         if(hours > 12){
17902             hours = hours - 12;
17903         }
17904         
17905         if(hours < 10){
17906             hours = '0' + hours;
17907         }
17908         
17909         if(minutes < 10){
17910             minutes = '0' + minutes;
17911         }
17912         
17913         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17914         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17915         this.pop.select('button', true).first().dom.innerHTML = period;
17916         
17917     },
17918     
17919     place: function()
17920     {   
17921         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17922         
17923         var cls = ['bottom'];
17924         
17925         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17926             cls.pop();
17927             cls.push('top');
17928         }
17929         
17930         cls.push('right');
17931         
17932         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17933             cls.pop();
17934             cls.push('left');
17935         }
17936         
17937         this.picker().addClass(cls.join('-'));
17938         
17939         var _this = this;
17940         
17941         Roo.each(cls, function(c){
17942             if(c == 'bottom'){
17943                 _this.picker().setTop(_this.inputEl().getHeight());
17944                 return;
17945             }
17946             if(c == 'top'){
17947                 _this.picker().setTop(0 - _this.picker().getHeight());
17948                 return;
17949             }
17950             
17951             if(c == 'left'){
17952                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17953                 return;
17954             }
17955             if(c == 'right'){
17956                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17957                 return;
17958             }
17959         });
17960         
17961     },
17962   
17963     onFocus : function()
17964     {
17965         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17966         this.show();
17967     },
17968     
17969     onBlur : function()
17970     {
17971         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17972         this.hide();
17973     },
17974     
17975     show : function()
17976     {
17977         this.picker().show();
17978         this.pop.show();
17979         this.update();
17980         this.place();
17981         
17982         this.fireEvent('show', this, this.date);
17983     },
17984     
17985     hide : function()
17986     {
17987         this.picker().hide();
17988         this.pop.hide();
17989         
17990         this.fireEvent('hide', this, this.date);
17991     },
17992     
17993     setTime : function()
17994     {
17995         this.hide();
17996         this.setValue(this.time.format(this.format));
17997         
17998         this.fireEvent('select', this, this.date);
17999         
18000         
18001     },
18002     
18003     onMousedown: function(e){
18004         e.stopPropagation();
18005         e.preventDefault();
18006     },
18007     
18008     onIncrementHours: function()
18009     {
18010         Roo.log('onIncrementHours');
18011         this.time = this.time.add(Date.HOUR, 1);
18012         this.update();
18013         
18014     },
18015     
18016     onDecrementHours: function()
18017     {
18018         Roo.log('onDecrementHours');
18019         this.time = this.time.add(Date.HOUR, -1);
18020         this.update();
18021     },
18022     
18023     onIncrementMinutes: function()
18024     {
18025         Roo.log('onIncrementMinutes');
18026         this.time = this.time.add(Date.MINUTE, 1);
18027         this.update();
18028     },
18029     
18030     onDecrementMinutes: function()
18031     {
18032         Roo.log('onDecrementMinutes');
18033         this.time = this.time.add(Date.MINUTE, -1);
18034         this.update();
18035     },
18036     
18037     onTogglePeriod: function()
18038     {
18039         Roo.log('onTogglePeriod');
18040         this.time = this.time.add(Date.HOUR, 12);
18041         this.update();
18042     }
18043     
18044    
18045 });
18046
18047 Roo.apply(Roo.bootstrap.TimeField,  {
18048     
18049     content : {
18050         tag: 'tbody',
18051         cn: [
18052             {
18053                 tag: 'tr',
18054                 cn: [
18055                 {
18056                     tag: 'td',
18057                     colspan: '7'
18058                 }
18059                 ]
18060             }
18061         ]
18062     },
18063     
18064     footer : {
18065         tag: 'tfoot',
18066         cn: [
18067             {
18068                 tag: 'tr',
18069                 cn: [
18070                 {
18071                     tag: 'th',
18072                     colspan: '7',
18073                     cls: '',
18074                     cn: [
18075                         {
18076                             tag: 'button',
18077                             cls: 'btn btn-info ok',
18078                             html: 'OK'
18079                         }
18080                     ]
18081                 }
18082
18083                 ]
18084             }
18085         ]
18086     }
18087 });
18088
18089 Roo.apply(Roo.bootstrap.TimeField,  {
18090   
18091     template : {
18092         tag: 'div',
18093         cls: 'datepicker dropdown-menu',
18094         cn: [
18095             {
18096                 tag: 'div',
18097                 cls: 'datepicker-time',
18098                 cn: [
18099                 {
18100                     tag: 'table',
18101                     cls: 'table-condensed',
18102                     cn:[
18103                     Roo.bootstrap.TimeField.content,
18104                     Roo.bootstrap.TimeField.footer
18105                     ]
18106                 }
18107                 ]
18108             }
18109         ]
18110     }
18111 });
18112
18113  
18114
18115  /*
18116  * - LGPL
18117  *
18118  * MonthField
18119  * 
18120  */
18121
18122 /**
18123  * @class Roo.bootstrap.MonthField
18124  * @extends Roo.bootstrap.Input
18125  * Bootstrap MonthField class
18126  * 
18127  * @cfg {String} language default en
18128  * 
18129  * @constructor
18130  * Create a new MonthField
18131  * @param {Object} config The config object
18132  */
18133
18134 Roo.bootstrap.MonthField = function(config){
18135     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18136     
18137     this.addEvents({
18138         /**
18139          * @event show
18140          * Fires when this field show.
18141          * @param {Roo.bootstrap.MonthField} this
18142          * @param {Mixed} date The date value
18143          */
18144         show : true,
18145         /**
18146          * @event show
18147          * Fires when this field hide.
18148          * @param {Roo.bootstrap.MonthField} this
18149          * @param {Mixed} date The date value
18150          */
18151         hide : true,
18152         /**
18153          * @event select
18154          * Fires when select a date.
18155          * @param {Roo.bootstrap.MonthField} this
18156          * @param {String} oldvalue The old value
18157          * @param {String} newvalue The new value
18158          */
18159         select : true
18160     });
18161 };
18162
18163 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18164     
18165     onRender: function(ct, position)
18166     {
18167         
18168         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18169         
18170         this.language = this.language || 'en';
18171         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18172         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18173         
18174         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18175         this.isInline = false;
18176         this.isInput = true;
18177         this.component = this.el.select('.add-on', true).first() || false;
18178         this.component = (this.component && this.component.length === 0) ? false : this.component;
18179         this.hasInput = this.component && this.inputEL().length;
18180         
18181         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18182         
18183         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18184         
18185         this.picker().on('mousedown', this.onMousedown, this);
18186         this.picker().on('click', this.onClick, this);
18187         
18188         this.picker().addClass('datepicker-dropdown');
18189         
18190         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18191             v.setStyle('width', '189px');
18192         });
18193         
18194         this.fillMonths();
18195         
18196         this.update();
18197         
18198         if(this.isInline) {
18199             this.show();
18200         }
18201         
18202     },
18203     
18204     setValue: function(v, suppressEvent)
18205     {   
18206         var o = this.getValue();
18207         
18208         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18209         
18210         this.update();
18211
18212         if(suppressEvent !== true){
18213             this.fireEvent('select', this, o, v);
18214         }
18215         
18216     },
18217     
18218     getValue: function()
18219     {
18220         return this.value;
18221     },
18222     
18223     onClick: function(e) 
18224     {
18225         e.stopPropagation();
18226         e.preventDefault();
18227         
18228         var target = e.getTarget();
18229         
18230         if(target.nodeName.toLowerCase() === 'i'){
18231             target = Roo.get(target).dom.parentNode;
18232         }
18233         
18234         var nodeName = target.nodeName;
18235         var className = target.className;
18236         var html = target.innerHTML;
18237         
18238         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18239             return;
18240         }
18241         
18242         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18243         
18244         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18245         
18246         this.hide();
18247                         
18248     },
18249     
18250     picker : function()
18251     {
18252         return this.pickerEl;
18253     },
18254     
18255     fillMonths: function()
18256     {    
18257         var i = 0;
18258         var months = this.picker().select('>.datepicker-months td', true).first();
18259         
18260         months.dom.innerHTML = '';
18261         
18262         while (i < 12) {
18263             var month = {
18264                 tag: 'span',
18265                 cls: 'month',
18266                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18267             };
18268             
18269             months.createChild(month);
18270         }
18271         
18272     },
18273     
18274     update: function()
18275     {
18276         var _this = this;
18277         
18278         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18279             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18280         }
18281         
18282         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18283             e.removeClass('active');
18284             
18285             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18286                 e.addClass('active');
18287             }
18288         })
18289     },
18290     
18291     place: function()
18292     {
18293         if(this.isInline) {
18294             return;
18295         }
18296         
18297         this.picker().removeClass(['bottom', 'top']);
18298         
18299         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18300             /*
18301              * place to the top of element!
18302              *
18303              */
18304             
18305             this.picker().addClass('top');
18306             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18307             
18308             return;
18309         }
18310         
18311         this.picker().addClass('bottom');
18312         
18313         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18314     },
18315     
18316     onFocus : function()
18317     {
18318         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18319         this.show();
18320     },
18321     
18322     onBlur : function()
18323     {
18324         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18325         
18326         var d = this.inputEl().getValue();
18327         
18328         this.setValue(d);
18329                 
18330         this.hide();
18331     },
18332     
18333     show : function()
18334     {
18335         this.picker().show();
18336         this.picker().select('>.datepicker-months', true).first().show();
18337         this.update();
18338         this.place();
18339         
18340         this.fireEvent('show', this, this.date);
18341     },
18342     
18343     hide : function()
18344     {
18345         if(this.isInline) {
18346             return;
18347         }
18348         this.picker().hide();
18349         this.fireEvent('hide', this, this.date);
18350         
18351     },
18352     
18353     onMousedown: function(e)
18354     {
18355         e.stopPropagation();
18356         e.preventDefault();
18357     },
18358     
18359     keyup: function(e)
18360     {
18361         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18362         this.update();
18363     },
18364
18365     fireKey: function(e)
18366     {
18367         if (!this.picker().isVisible()){
18368             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18369                 this.show();
18370             }
18371             return;
18372         }
18373         
18374         var dir;
18375         
18376         switch(e.keyCode){
18377             case 27: // escape
18378                 this.hide();
18379                 e.preventDefault();
18380                 break;
18381             case 37: // left
18382             case 39: // right
18383                 dir = e.keyCode == 37 ? -1 : 1;
18384                 
18385                 this.vIndex = this.vIndex + dir;
18386                 
18387                 if(this.vIndex < 0){
18388                     this.vIndex = 0;
18389                 }
18390                 
18391                 if(this.vIndex > 11){
18392                     this.vIndex = 11;
18393                 }
18394                 
18395                 if(isNaN(this.vIndex)){
18396                     this.vIndex = 0;
18397                 }
18398                 
18399                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18400                 
18401                 break;
18402             case 38: // up
18403             case 40: // down
18404                 
18405                 dir = e.keyCode == 38 ? -1 : 1;
18406                 
18407                 this.vIndex = this.vIndex + dir * 4;
18408                 
18409                 if(this.vIndex < 0){
18410                     this.vIndex = 0;
18411                 }
18412                 
18413                 if(this.vIndex > 11){
18414                     this.vIndex = 11;
18415                 }
18416                 
18417                 if(isNaN(this.vIndex)){
18418                     this.vIndex = 0;
18419                 }
18420                 
18421                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18422                 break;
18423                 
18424             case 13: // enter
18425                 
18426                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18427                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18428                 }
18429                 
18430                 this.hide();
18431                 e.preventDefault();
18432                 break;
18433             case 9: // tab
18434                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18435                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18436                 }
18437                 this.hide();
18438                 break;
18439             case 16: // shift
18440             case 17: // ctrl
18441             case 18: // alt
18442                 break;
18443             default :
18444                 this.hide();
18445                 
18446         }
18447     },
18448     
18449     remove: function() 
18450     {
18451         this.picker().remove();
18452     }
18453    
18454 });
18455
18456 Roo.apply(Roo.bootstrap.MonthField,  {
18457     
18458     content : {
18459         tag: 'tbody',
18460         cn: [
18461         {
18462             tag: 'tr',
18463             cn: [
18464             {
18465                 tag: 'td',
18466                 colspan: '7'
18467             }
18468             ]
18469         }
18470         ]
18471     },
18472     
18473     dates:{
18474         en: {
18475             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18476             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18477         }
18478     }
18479 });
18480
18481 Roo.apply(Roo.bootstrap.MonthField,  {
18482   
18483     template : {
18484         tag: 'div',
18485         cls: 'datepicker dropdown-menu roo-dynamic',
18486         cn: [
18487             {
18488                 tag: 'div',
18489                 cls: 'datepicker-months',
18490                 cn: [
18491                 {
18492                     tag: 'table',
18493                     cls: 'table-condensed',
18494                     cn:[
18495                         Roo.bootstrap.DateField.content
18496                     ]
18497                 }
18498                 ]
18499             }
18500         ]
18501     }
18502 });
18503
18504  
18505
18506  
18507  /*
18508  * - LGPL
18509  *
18510  * CheckBox
18511  * 
18512  */
18513
18514 /**
18515  * @class Roo.bootstrap.CheckBox
18516  * @extends Roo.bootstrap.Input
18517  * Bootstrap CheckBox class
18518  * 
18519  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18520  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18521  * @cfg {String} boxLabel The text that appears beside the checkbox
18522  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18523  * @cfg {Boolean} checked initnal the element
18524  * @cfg {Boolean} inline inline the element (default false)
18525  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18526  * 
18527  * @constructor
18528  * Create a new CheckBox
18529  * @param {Object} config The config object
18530  */
18531
18532 Roo.bootstrap.CheckBox = function(config){
18533     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18534    
18535     this.addEvents({
18536         /**
18537         * @event check
18538         * Fires when the element is checked or unchecked.
18539         * @param {Roo.bootstrap.CheckBox} this This input
18540         * @param {Boolean} checked The new checked value
18541         */
18542        check : true
18543     });
18544     
18545 };
18546
18547 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18548   
18549     inputType: 'checkbox',
18550     inputValue: 1,
18551     valueOff: 0,
18552     boxLabel: false,
18553     checked: false,
18554     weight : false,
18555     inline: false,
18556     
18557     getAutoCreate : function()
18558     {
18559         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18560         
18561         var id = Roo.id();
18562         
18563         var cfg = {};
18564         
18565         cfg.cls = 'form-group ' + this.inputType; //input-group
18566         
18567         if(this.inline){
18568             cfg.cls += ' ' + this.inputType + '-inline';
18569         }
18570         
18571         var input =  {
18572             tag: 'input',
18573             id : id,
18574             type : this.inputType,
18575             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18576             cls : 'roo-' + this.inputType, //'form-box',
18577             placeholder : this.placeholder || ''
18578             
18579         };
18580         
18581         if (this.weight) { // Validity check?
18582             cfg.cls += " " + this.inputType + "-" + this.weight;
18583         }
18584         
18585         if (this.disabled) {
18586             input.disabled=true;
18587         }
18588         
18589         if(this.checked){
18590             input.checked = this.checked;
18591         }
18592         
18593         if (this.name) {
18594             input.name = this.name;
18595         }
18596         
18597         if (this.size) {
18598             input.cls += ' input-' + this.size;
18599         }
18600         
18601         var settings=this;
18602         
18603         ['xs','sm','md','lg'].map(function(size){
18604             if (settings[size]) {
18605                 cfg.cls += ' col-' + size + '-' + settings[size];
18606             }
18607         });
18608         
18609         var inputblock = input;
18610          
18611         if (this.before || this.after) {
18612             
18613             inputblock = {
18614                 cls : 'input-group',
18615                 cn :  [] 
18616             };
18617             
18618             if (this.before) {
18619                 inputblock.cn.push({
18620                     tag :'span',
18621                     cls : 'input-group-addon',
18622                     html : this.before
18623                 });
18624             }
18625             
18626             inputblock.cn.push(input);
18627             
18628             if (this.after) {
18629                 inputblock.cn.push({
18630                     tag :'span',
18631                     cls : 'input-group-addon',
18632                     html : this.after
18633                 });
18634             }
18635             
18636         }
18637         
18638         if (align ==='left' && this.fieldLabel.length) {
18639 //                Roo.log("left and has label");
18640                 cfg.cn = [
18641                     
18642                     {
18643                         tag: 'label',
18644                         'for' :  id,
18645                         cls : 'control-label col-md-' + this.labelWidth,
18646                         html : this.fieldLabel
18647                         
18648                     },
18649                     {
18650                         cls : "col-md-" + (12 - this.labelWidth), 
18651                         cn: [
18652                             inputblock
18653                         ]
18654                     }
18655                     
18656                 ];
18657         } else if ( this.fieldLabel.length) {
18658 //                Roo.log(" label");
18659                 cfg.cn = [
18660                    
18661                     {
18662                         tag: this.boxLabel ? 'span' : 'label',
18663                         'for': id,
18664                         cls: 'control-label box-input-label',
18665                         //cls : 'input-group-addon',
18666                         html : this.fieldLabel
18667                         
18668                     },
18669                     
18670                     inputblock
18671                     
18672                 ];
18673
18674         } else {
18675             
18676 //                Roo.log(" no label && no align");
18677                 cfg.cn = [  inputblock ] ;
18678                 
18679                 
18680         }
18681         if(this.boxLabel){
18682              var boxLabelCfg = {
18683                 tag: 'label',
18684                 //'for': id, // box label is handled by onclick - so no for...
18685                 cls: 'box-label',
18686                 html: this.boxLabel
18687             };
18688             
18689             if(this.tooltip){
18690                 boxLabelCfg.tooltip = this.tooltip;
18691             }
18692              
18693             cfg.cn.push(boxLabelCfg);
18694         }
18695         
18696         
18697        
18698         return cfg;
18699         
18700     },
18701     
18702     /**
18703      * return the real input element.
18704      */
18705     inputEl: function ()
18706     {
18707         return this.el.select('input.roo-' + this.inputType,true).first();
18708     },
18709     
18710     labelEl: function()
18711     {
18712         return this.el.select('label.control-label',true).first();
18713     },
18714     /* depricated... */
18715     
18716     label: function()
18717     {
18718         return this.labelEl();
18719     },
18720     
18721     initEvents : function()
18722     {
18723 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18724         
18725         this.inputEl().on('click', this.onClick,  this);
18726         
18727         if (this.boxLabel) { 
18728             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18729         }
18730         
18731         this.startValue = this.getValue();
18732         
18733         if(this.groupId){
18734             Roo.bootstrap.CheckBox.register(this);
18735         }
18736     },
18737     
18738     onClick : function()
18739     {   
18740         this.setChecked(!this.checked);
18741     },
18742     
18743     setChecked : function(state,suppressEvent)
18744     {
18745         this.startValue = this.getValue();
18746         
18747         if(this.inputType == 'radio'){
18748             
18749             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18750                 e.dom.checked = false;
18751             });
18752             
18753             this.inputEl().dom.checked = true;
18754             
18755             this.inputEl().dom.value = this.inputValue;
18756             
18757             if(suppressEvent !== true){
18758                 this.fireEvent('check', this, true);
18759             }
18760             
18761             this.validate();
18762             
18763             return;
18764         }
18765         
18766         this.checked = state;
18767         
18768         this.inputEl().dom.checked = state;
18769         
18770         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18771         
18772         if(suppressEvent !== true){
18773             this.fireEvent('check', this, state);
18774         }
18775         
18776         this.validate();
18777     },
18778     
18779     getValue : function()
18780     {
18781         if(this.inputType == 'radio'){
18782             return this.getGroupValue();
18783         }
18784         
18785         return this.inputEl().getValue();
18786         
18787     },
18788     
18789     getGroupValue : function()
18790     {
18791         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18792             return '';
18793         }
18794         
18795         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18796     },
18797     
18798     setValue : function(v,suppressEvent)
18799     {
18800         if(this.inputType == 'radio'){
18801             this.setGroupValue(v, suppressEvent);
18802             return;
18803         }
18804         
18805         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18806         
18807         this.validate();
18808     },
18809     
18810     setGroupValue : function(v, suppressEvent)
18811     {
18812         this.startValue = this.getValue();
18813         
18814         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18815             e.dom.checked = false;
18816             
18817             if(e.dom.value == v){
18818                 e.dom.checked = true;
18819             }
18820         });
18821         
18822         if(suppressEvent !== true){
18823             this.fireEvent('check', this, true);
18824         }
18825
18826         this.validate();
18827         
18828         return;
18829     },
18830     
18831     validate : function()
18832     {
18833         if(
18834                 this.disabled || 
18835                 (this.inputType == 'radio' && this.validateRadio()) ||
18836                 (this.inputType == 'checkbox' && this.validateCheckbox())
18837         ){
18838             this.markValid();
18839             return true;
18840         }
18841         
18842         this.markInvalid();
18843         return false;
18844     },
18845     
18846     validateRadio : function()
18847     {
18848         var valid = false;
18849         
18850         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18851             if(!e.dom.checked){
18852                 return;
18853             }
18854             
18855             valid = true;
18856             
18857             return false;
18858         });
18859         
18860         return valid;
18861     },
18862     
18863     validateCheckbox : function()
18864     {
18865         if(!this.groupId){
18866             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18867         }
18868         
18869         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18870         
18871         if(!group){
18872             return false;
18873         }
18874         
18875         var r = false;
18876         
18877         for(var i in group){
18878             if(r){
18879                 break;
18880             }
18881             
18882             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18883         }
18884         
18885         return r;
18886     },
18887     
18888     /**
18889      * Mark this field as valid
18890      */
18891     markValid : function()
18892     {
18893         if(this.allowBlank){
18894             return;
18895         }
18896         
18897         var _this = this;
18898         
18899         this.fireEvent('valid', this);
18900         
18901         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18902         
18903         if(this.groupId){
18904             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18905         }
18906         
18907         if(label){
18908             label.markValid();
18909         }
18910         
18911         if(this.inputType == 'radio'){
18912             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18913                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18914                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18915             });
18916             
18917             return;
18918         }
18919         
18920         if(!this.groupId){
18921             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18922             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18923             return;
18924         }
18925         
18926         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18927             
18928         if(!group){
18929             return;
18930         }
18931         
18932         for(var i in group){
18933             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18934             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18935         }
18936     },
18937     
18938      /**
18939      * Mark this field as invalid
18940      * @param {String} msg The validation message
18941      */
18942     markInvalid : function(msg)
18943     {
18944         if(this.allowBlank){
18945             return;
18946         }
18947         
18948         var _this = this;
18949         
18950         this.fireEvent('invalid', this, msg);
18951         
18952         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18953         
18954         if(this.groupId){
18955             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18956         }
18957         
18958         if(label){
18959             label.markInvalid();
18960         }
18961             
18962         if(this.inputType == 'radio'){
18963             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18964                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18965                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18966             });
18967             
18968             return;
18969         }
18970         
18971         if(!this.groupId){
18972             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18973             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18974             return;
18975         }
18976         
18977         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18978         
18979         if(!group){
18980             return;
18981         }
18982         
18983         for(var i in group){
18984             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18985             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18986         }
18987         
18988     }
18989     
18990 });
18991
18992 Roo.apply(Roo.bootstrap.CheckBox, {
18993     
18994     groups: {},
18995     
18996      /**
18997     * register a CheckBox Group
18998     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18999     */
19000     register : function(checkbox)
19001     {
19002         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19003             this.groups[checkbox.groupId] = {};
19004         }
19005         
19006         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19007             return;
19008         }
19009         
19010         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19011         
19012     },
19013     /**
19014     * fetch a CheckBox Group based on the group ID
19015     * @param {string} the group ID
19016     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19017     */
19018     get: function(groupId) {
19019         if (typeof(this.groups[groupId]) == 'undefined') {
19020             return false;
19021         }
19022         
19023         return this.groups[groupId] ;
19024     }
19025     
19026     
19027 });
19028 /*
19029  * - LGPL
19030  *
19031  * Radio
19032  *
19033  *
19034  * not inline
19035  *<div class="radio">
19036   <label>
19037     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19038     Option one is this and that&mdash;be sure to include why it's great
19039   </label>
19040 </div>
19041  *
19042  *
19043  *inline
19044  *<span>
19045  *<label class="radio-inline">fieldLabel</label>
19046  *<label class="radio-inline">
19047   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19048 </label>
19049 <span>
19050  * 
19051  * 
19052  */
19053
19054 /**
19055  * @class Roo.bootstrap.Radio
19056  * @extends Roo.bootstrap.CheckBox
19057  * Bootstrap Radio class
19058
19059  * @constructor
19060  * Create a new Radio
19061  * @param {Object} config The config object
19062  */
19063
19064 Roo.bootstrap.Radio = function(config){
19065     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19066    
19067 };
19068
19069 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19070     
19071     inputType: 'radio',
19072     inputValue: '',
19073     valueOff: '',
19074     
19075     getAutoCreate : function()
19076     {
19077         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19078         align = align || 'left'; // default...
19079         
19080         
19081         
19082         var id = Roo.id();
19083         
19084         var cfg = {
19085                 tag : this.inline ? 'span' : 'div',
19086                 cls : '',
19087                 cn : []
19088         };
19089         
19090         var inline = this.inline ? ' radio-inline' : '';
19091         
19092         var lbl = {
19093                 tag: 'label' ,
19094                 // does not need for, as we wrap the input with it..
19095                 'for' : id,
19096                 cls : 'control-label box-label' + inline,
19097                 cn : []
19098         };
19099         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19100         
19101         var fieldLabel = {
19102             tag: 'label' ,
19103             //cls : 'control-label' + inline,
19104             html : this.fieldLabel,
19105             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19106         };
19107         
19108  
19109         
19110         
19111         var input =  {
19112             tag: 'input',
19113             id : id,
19114             type : this.inputType,
19115             //value : (!this.checked) ? this.valueOff : this.inputValue,
19116             value : this.inputValue,
19117             cls : 'roo-radio',
19118             placeholder : this.placeholder || '' // ?? needed????
19119             
19120         };
19121         if (this.weight) { // Validity check?
19122             input.cls += " radio-" + this.weight;
19123         }
19124         if (this.disabled) {
19125             input.disabled=true;
19126         }
19127         
19128         if(this.checked){
19129             input.checked = this.checked;
19130         }
19131         
19132         if (this.name) {
19133             input.name = this.name;
19134         }
19135         
19136         if (this.size) {
19137             input.cls += ' input-' + this.size;
19138         }
19139         
19140         //?? can span's inline have a width??
19141         
19142         var settings=this;
19143         ['xs','sm','md','lg'].map(function(size){
19144             if (settings[size]) {
19145                 cfg.cls += ' col-' + size + '-' + settings[size];
19146             }
19147         });
19148         
19149         var inputblock = input;
19150         
19151         if (this.before || this.after) {
19152             
19153             inputblock = {
19154                 cls : 'input-group',
19155                 tag : 'span',
19156                 cn :  [] 
19157             };
19158             if (this.before) {
19159                 inputblock.cn.push({
19160                     tag :'span',
19161                     cls : 'input-group-addon',
19162                     html : this.before
19163                 });
19164             }
19165             inputblock.cn.push(input);
19166             if (this.after) {
19167                 inputblock.cn.push({
19168                     tag :'span',
19169                     cls : 'input-group-addon',
19170                     html : this.after
19171                 });
19172             }
19173             
19174         };
19175         
19176         
19177         if (this.fieldLabel && this.fieldLabel.length) {
19178             cfg.cn.push(fieldLabel);
19179         }
19180        
19181         // normal bootstrap puts the input inside the label.
19182         // however with our styled version - it has to go after the input.
19183        
19184         //lbl.cn.push(inputblock);
19185         
19186         var lblwrap =  {
19187             tag: 'span',
19188             cls: 'radio' + inline,
19189             cn: [
19190                 inputblock,
19191                 lbl
19192             ]
19193         };
19194         
19195         cfg.cn.push( lblwrap);
19196         
19197         if(this.boxLabel){
19198             lbl.cn.push({
19199                 tag: 'span',
19200                 html: this.boxLabel
19201             })
19202         }
19203          
19204         
19205         return cfg;
19206         
19207     },
19208     
19209     initEvents : function()
19210     {
19211 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19212         
19213         this.inputEl().on('click', this.onClick,  this);
19214         if (this.boxLabel) {
19215             //Roo.log('find label');
19216             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19217         }
19218         
19219     },
19220     
19221     inputEl: function ()
19222     {
19223         return this.el.select('input.roo-radio',true).first();
19224     },
19225     onClick : function()
19226     {   
19227         Roo.log("click");
19228         this.setChecked(true);
19229     },
19230     
19231     setChecked : function(state,suppressEvent)
19232     {
19233         if(state){
19234             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19235                 v.dom.checked = false;
19236             });
19237         }
19238         Roo.log(this.inputEl().dom);
19239         this.checked = state;
19240         this.inputEl().dom.checked = state;
19241         
19242         if(suppressEvent !== true){
19243             this.fireEvent('check', this, state);
19244         }
19245         
19246         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19247         
19248     },
19249     
19250     getGroupValue : function()
19251     {
19252         var value = '';
19253         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19254             if(v.dom.checked == true){
19255                 value = v.dom.value;
19256             }
19257         });
19258         
19259         return value;
19260     },
19261     
19262     /**
19263      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19264      * @return {Mixed} value The field value
19265      */
19266     getValue : function(){
19267         return this.getGroupValue();
19268     }
19269     
19270 });
19271
19272  
19273 //<script type="text/javascript">
19274
19275 /*
19276  * Based  Ext JS Library 1.1.1
19277  * Copyright(c) 2006-2007, Ext JS, LLC.
19278  * LGPL
19279  *
19280  */
19281  
19282 /**
19283  * @class Roo.HtmlEditorCore
19284  * @extends Roo.Component
19285  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19286  *
19287  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19288  */
19289
19290 Roo.HtmlEditorCore = function(config){
19291     
19292     
19293     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19294     
19295     
19296     this.addEvents({
19297         /**
19298          * @event initialize
19299          * Fires when the editor is fully initialized (including the iframe)
19300          * @param {Roo.HtmlEditorCore} this
19301          */
19302         initialize: true,
19303         /**
19304          * @event activate
19305          * Fires when the editor is first receives the focus. Any insertion must wait
19306          * until after this event.
19307          * @param {Roo.HtmlEditorCore} this
19308          */
19309         activate: true,
19310          /**
19311          * @event beforesync
19312          * Fires before the textarea is updated with content from the editor iframe. Return false
19313          * to cancel the sync.
19314          * @param {Roo.HtmlEditorCore} this
19315          * @param {String} html
19316          */
19317         beforesync: true,
19318          /**
19319          * @event beforepush
19320          * Fires before the iframe editor is updated with content from the textarea. Return false
19321          * to cancel the push.
19322          * @param {Roo.HtmlEditorCore} this
19323          * @param {String} html
19324          */
19325         beforepush: true,
19326          /**
19327          * @event sync
19328          * Fires when the textarea is updated with content from the editor iframe.
19329          * @param {Roo.HtmlEditorCore} this
19330          * @param {String} html
19331          */
19332         sync: true,
19333          /**
19334          * @event push
19335          * Fires when the iframe editor is updated with content from the textarea.
19336          * @param {Roo.HtmlEditorCore} this
19337          * @param {String} html
19338          */
19339         push: true,
19340         
19341         /**
19342          * @event editorevent
19343          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19344          * @param {Roo.HtmlEditorCore} this
19345          */
19346         editorevent: true
19347         
19348     });
19349     
19350     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19351     
19352     // defaults : white / black...
19353     this.applyBlacklists();
19354     
19355     
19356     
19357 };
19358
19359
19360 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19361
19362
19363      /**
19364      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19365      */
19366     
19367     owner : false,
19368     
19369      /**
19370      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19371      *                        Roo.resizable.
19372      */
19373     resizable : false,
19374      /**
19375      * @cfg {Number} height (in pixels)
19376      */   
19377     height: 300,
19378    /**
19379      * @cfg {Number} width (in pixels)
19380      */   
19381     width: 500,
19382     
19383     /**
19384      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19385      * 
19386      */
19387     stylesheets: false,
19388     
19389     // id of frame..
19390     frameId: false,
19391     
19392     // private properties
19393     validationEvent : false,
19394     deferHeight: true,
19395     initialized : false,
19396     activated : false,
19397     sourceEditMode : false,
19398     onFocus : Roo.emptyFn,
19399     iframePad:3,
19400     hideMode:'offsets',
19401     
19402     clearUp: true,
19403     
19404     // blacklist + whitelisted elements..
19405     black: false,
19406     white: false,
19407      
19408     
19409
19410     /**
19411      * Protected method that will not generally be called directly. It
19412      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19413      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19414      */
19415     getDocMarkup : function(){
19416         // body styles..
19417         var st = '';
19418         
19419         // inherit styels from page...?? 
19420         if (this.stylesheets === false) {
19421             
19422             Roo.get(document.head).select('style').each(function(node) {
19423                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19424             });
19425             
19426             Roo.get(document.head).select('link').each(function(node) { 
19427                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19428             });
19429             
19430         } else if (!this.stylesheets.length) {
19431                 // simple..
19432                 st = '<style type="text/css">' +
19433                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19434                    '</style>';
19435         } else { 
19436             
19437         }
19438         
19439         st +=  '<style type="text/css">' +
19440             'IMG { cursor: pointer } ' +
19441         '</style>';
19442
19443         
19444         return '<html><head>' + st  +
19445             //<style type="text/css">' +
19446             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19447             //'</style>' +
19448             ' </head><body class="roo-htmleditor-body"></body></html>';
19449     },
19450
19451     // private
19452     onRender : function(ct, position)
19453     {
19454         var _t = this;
19455         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19456         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19457         
19458         
19459         this.el.dom.style.border = '0 none';
19460         this.el.dom.setAttribute('tabIndex', -1);
19461         this.el.addClass('x-hidden hide');
19462         
19463         
19464         
19465         if(Roo.isIE){ // fix IE 1px bogus margin
19466             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19467         }
19468        
19469         
19470         this.frameId = Roo.id();
19471         
19472          
19473         
19474         var iframe = this.owner.wrap.createChild({
19475             tag: 'iframe',
19476             cls: 'form-control', // bootstrap..
19477             id: this.frameId,
19478             name: this.frameId,
19479             frameBorder : 'no',
19480             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19481         }, this.el
19482         );
19483         
19484         
19485         this.iframe = iframe.dom;
19486
19487          this.assignDocWin();
19488         
19489         this.doc.designMode = 'on';
19490        
19491         this.doc.open();
19492         this.doc.write(this.getDocMarkup());
19493         this.doc.close();
19494
19495         
19496         var task = { // must defer to wait for browser to be ready
19497             run : function(){
19498                 //console.log("run task?" + this.doc.readyState);
19499                 this.assignDocWin();
19500                 if(this.doc.body || this.doc.readyState == 'complete'){
19501                     try {
19502                         this.doc.designMode="on";
19503                     } catch (e) {
19504                         return;
19505                     }
19506                     Roo.TaskMgr.stop(task);
19507                     this.initEditor.defer(10, this);
19508                 }
19509             },
19510             interval : 10,
19511             duration: 10000,
19512             scope: this
19513         };
19514         Roo.TaskMgr.start(task);
19515
19516     },
19517
19518     // private
19519     onResize : function(w, h)
19520     {
19521          Roo.log('resize: ' +w + ',' + h );
19522         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19523         if(!this.iframe){
19524             return;
19525         }
19526         if(typeof w == 'number'){
19527             
19528             this.iframe.style.width = w + 'px';
19529         }
19530         if(typeof h == 'number'){
19531             
19532             this.iframe.style.height = h + 'px';
19533             if(this.doc){
19534                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19535             }
19536         }
19537         
19538     },
19539
19540     /**
19541      * Toggles the editor between standard and source edit mode.
19542      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19543      */
19544     toggleSourceEdit : function(sourceEditMode){
19545         
19546         this.sourceEditMode = sourceEditMode === true;
19547         
19548         if(this.sourceEditMode){
19549  
19550             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19551             
19552         }else{
19553             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19554             //this.iframe.className = '';
19555             this.deferFocus();
19556         }
19557         //this.setSize(this.owner.wrap.getSize());
19558         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19559     },
19560
19561     
19562   
19563
19564     /**
19565      * Protected method that will not generally be called directly. If you need/want
19566      * custom HTML cleanup, this is the method you should override.
19567      * @param {String} html The HTML to be cleaned
19568      * return {String} The cleaned HTML
19569      */
19570     cleanHtml : function(html){
19571         html = String(html);
19572         if(html.length > 5){
19573             if(Roo.isSafari){ // strip safari nonsense
19574                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19575             }
19576         }
19577         if(html == '&nbsp;'){
19578             html = '';
19579         }
19580         return html;
19581     },
19582
19583     /**
19584      * HTML Editor -> Textarea
19585      * Protected method that will not generally be called directly. Syncs the contents
19586      * of the editor iframe with the textarea.
19587      */
19588     syncValue : function(){
19589         if(this.initialized){
19590             var bd = (this.doc.body || this.doc.documentElement);
19591             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19592             var html = bd.innerHTML;
19593             if(Roo.isSafari){
19594                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19595                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19596                 if(m && m[1]){
19597                     html = '<div style="'+m[0]+'">' + html + '</div>';
19598                 }
19599             }
19600             html = this.cleanHtml(html);
19601             // fix up the special chars.. normaly like back quotes in word...
19602             // however we do not want to do this with chinese..
19603             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19604                 var cc = b.charCodeAt();
19605                 if (
19606                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19607                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19608                     (cc >= 0xf900 && cc < 0xfb00 )
19609                 ) {
19610                         return b;
19611                 }
19612                 return "&#"+cc+";" 
19613             });
19614             if(this.owner.fireEvent('beforesync', this, html) !== false){
19615                 this.el.dom.value = html;
19616                 this.owner.fireEvent('sync', this, html);
19617             }
19618         }
19619     },
19620
19621     /**
19622      * Protected method that will not generally be called directly. Pushes the value of the textarea
19623      * into the iframe editor.
19624      */
19625     pushValue : function(){
19626         if(this.initialized){
19627             var v = this.el.dom.value.trim();
19628             
19629 //            if(v.length < 1){
19630 //                v = '&#160;';
19631 //            }
19632             
19633             if(this.owner.fireEvent('beforepush', this, v) !== false){
19634                 var d = (this.doc.body || this.doc.documentElement);
19635                 d.innerHTML = v;
19636                 this.cleanUpPaste();
19637                 this.el.dom.value = d.innerHTML;
19638                 this.owner.fireEvent('push', this, v);
19639             }
19640         }
19641     },
19642
19643     // private
19644     deferFocus : function(){
19645         this.focus.defer(10, this);
19646     },
19647
19648     // doc'ed in Field
19649     focus : function(){
19650         if(this.win && !this.sourceEditMode){
19651             this.win.focus();
19652         }else{
19653             this.el.focus();
19654         }
19655     },
19656     
19657     assignDocWin: function()
19658     {
19659         var iframe = this.iframe;
19660         
19661          if(Roo.isIE){
19662             this.doc = iframe.contentWindow.document;
19663             this.win = iframe.contentWindow;
19664         } else {
19665 //            if (!Roo.get(this.frameId)) {
19666 //                return;
19667 //            }
19668 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19669 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19670             
19671             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19672                 return;
19673             }
19674             
19675             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19676             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19677         }
19678     },
19679     
19680     // private
19681     initEditor : function(){
19682         //console.log("INIT EDITOR");
19683         this.assignDocWin();
19684         
19685         
19686         
19687         this.doc.designMode="on";
19688         this.doc.open();
19689         this.doc.write(this.getDocMarkup());
19690         this.doc.close();
19691         
19692         var dbody = (this.doc.body || this.doc.documentElement);
19693         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19694         // this copies styles from the containing element into thsi one..
19695         // not sure why we need all of this..
19696         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19697         
19698         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19699         //ss['background-attachment'] = 'fixed'; // w3c
19700         dbody.bgProperties = 'fixed'; // ie
19701         //Roo.DomHelper.applyStyles(dbody, ss);
19702         Roo.EventManager.on(this.doc, {
19703             //'mousedown': this.onEditorEvent,
19704             'mouseup': this.onEditorEvent,
19705             'dblclick': this.onEditorEvent,
19706             'click': this.onEditorEvent,
19707             'keyup': this.onEditorEvent,
19708             buffer:100,
19709             scope: this
19710         });
19711         if(Roo.isGecko){
19712             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19713         }
19714         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19715             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19716         }
19717         this.initialized = true;
19718
19719         this.owner.fireEvent('initialize', this);
19720         this.pushValue();
19721     },
19722
19723     // private
19724     onDestroy : function(){
19725         
19726         
19727         
19728         if(this.rendered){
19729             
19730             //for (var i =0; i < this.toolbars.length;i++) {
19731             //    // fixme - ask toolbars for heights?
19732             //    this.toolbars[i].onDestroy();
19733            // }
19734             
19735             //this.wrap.dom.innerHTML = '';
19736             //this.wrap.remove();
19737         }
19738     },
19739
19740     // private
19741     onFirstFocus : function(){
19742         
19743         this.assignDocWin();
19744         
19745         
19746         this.activated = true;
19747          
19748     
19749         if(Roo.isGecko){ // prevent silly gecko errors
19750             this.win.focus();
19751             var s = this.win.getSelection();
19752             if(!s.focusNode || s.focusNode.nodeType != 3){
19753                 var r = s.getRangeAt(0);
19754                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19755                 r.collapse(true);
19756                 this.deferFocus();
19757             }
19758             try{
19759                 this.execCmd('useCSS', true);
19760                 this.execCmd('styleWithCSS', false);
19761             }catch(e){}
19762         }
19763         this.owner.fireEvent('activate', this);
19764     },
19765
19766     // private
19767     adjustFont: function(btn){
19768         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19769         //if(Roo.isSafari){ // safari
19770         //    adjust *= 2;
19771        // }
19772         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19773         if(Roo.isSafari){ // safari
19774             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19775             v =  (v < 10) ? 10 : v;
19776             v =  (v > 48) ? 48 : v;
19777             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19778             
19779         }
19780         
19781         
19782         v = Math.max(1, v+adjust);
19783         
19784         this.execCmd('FontSize', v  );
19785     },
19786
19787     onEditorEvent : function(e)
19788     {
19789         this.owner.fireEvent('editorevent', this, e);
19790       //  this.updateToolbar();
19791         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19792     },
19793
19794     insertTag : function(tg)
19795     {
19796         // could be a bit smarter... -> wrap the current selected tRoo..
19797         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19798             
19799             range = this.createRange(this.getSelection());
19800             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19801             wrappingNode.appendChild(range.extractContents());
19802             range.insertNode(wrappingNode);
19803
19804             return;
19805             
19806             
19807             
19808         }
19809         this.execCmd("formatblock",   tg);
19810         
19811     },
19812     
19813     insertText : function(txt)
19814     {
19815         
19816         
19817         var range = this.createRange();
19818         range.deleteContents();
19819                //alert(Sender.getAttribute('label'));
19820                
19821         range.insertNode(this.doc.createTextNode(txt));
19822     } ,
19823     
19824      
19825
19826     /**
19827      * Executes a Midas editor command on the editor document and performs necessary focus and
19828      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19829      * @param {String} cmd The Midas command
19830      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19831      */
19832     relayCmd : function(cmd, value){
19833         this.win.focus();
19834         this.execCmd(cmd, value);
19835         this.owner.fireEvent('editorevent', this);
19836         //this.updateToolbar();
19837         this.owner.deferFocus();
19838     },
19839
19840     /**
19841      * Executes a Midas editor command directly on the editor document.
19842      * For visual commands, you should use {@link #relayCmd} instead.
19843      * <b>This should only be called after the editor is initialized.</b>
19844      * @param {String} cmd The Midas command
19845      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19846      */
19847     execCmd : function(cmd, value){
19848         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19849         this.syncValue();
19850     },
19851  
19852  
19853    
19854     /**
19855      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19856      * to insert tRoo.
19857      * @param {String} text | dom node.. 
19858      */
19859     insertAtCursor : function(text)
19860     {
19861         
19862         
19863         
19864         if(!this.activated){
19865             return;
19866         }
19867         /*
19868         if(Roo.isIE){
19869             this.win.focus();
19870             var r = this.doc.selection.createRange();
19871             if(r){
19872                 r.collapse(true);
19873                 r.pasteHTML(text);
19874                 this.syncValue();
19875                 this.deferFocus();
19876             
19877             }
19878             return;
19879         }
19880         */
19881         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19882             this.win.focus();
19883             
19884             
19885             // from jquery ui (MIT licenced)
19886             var range, node;
19887             var win = this.win;
19888             
19889             if (win.getSelection && win.getSelection().getRangeAt) {
19890                 range = win.getSelection().getRangeAt(0);
19891                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19892                 range.insertNode(node);
19893             } else if (win.document.selection && win.document.selection.createRange) {
19894                 // no firefox support
19895                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19896                 win.document.selection.createRange().pasteHTML(txt);
19897             } else {
19898                 // no firefox support
19899                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19900                 this.execCmd('InsertHTML', txt);
19901             } 
19902             
19903             this.syncValue();
19904             
19905             this.deferFocus();
19906         }
19907     },
19908  // private
19909     mozKeyPress : function(e){
19910         if(e.ctrlKey){
19911             var c = e.getCharCode(), cmd;
19912           
19913             if(c > 0){
19914                 c = String.fromCharCode(c).toLowerCase();
19915                 switch(c){
19916                     case 'b':
19917                         cmd = 'bold';
19918                         break;
19919                     case 'i':
19920                         cmd = 'italic';
19921                         break;
19922                     
19923                     case 'u':
19924                         cmd = 'underline';
19925                         break;
19926                     
19927                     case 'v':
19928                         this.cleanUpPaste.defer(100, this);
19929                         return;
19930                         
19931                 }
19932                 if(cmd){
19933                     this.win.focus();
19934                     this.execCmd(cmd);
19935                     this.deferFocus();
19936                     e.preventDefault();
19937                 }
19938                 
19939             }
19940         }
19941     },
19942
19943     // private
19944     fixKeys : function(){ // load time branching for fastest keydown performance
19945         if(Roo.isIE){
19946             return function(e){
19947                 var k = e.getKey(), r;
19948                 if(k == e.TAB){
19949                     e.stopEvent();
19950                     r = this.doc.selection.createRange();
19951                     if(r){
19952                         r.collapse(true);
19953                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19954                         this.deferFocus();
19955                     }
19956                     return;
19957                 }
19958                 
19959                 if(k == e.ENTER){
19960                     r = this.doc.selection.createRange();
19961                     if(r){
19962                         var target = r.parentElement();
19963                         if(!target || target.tagName.toLowerCase() != 'li'){
19964                             e.stopEvent();
19965                             r.pasteHTML('<br />');
19966                             r.collapse(false);
19967                             r.select();
19968                         }
19969                     }
19970                 }
19971                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19972                     this.cleanUpPaste.defer(100, this);
19973                     return;
19974                 }
19975                 
19976                 
19977             };
19978         }else if(Roo.isOpera){
19979             return function(e){
19980                 var k = e.getKey();
19981                 if(k == e.TAB){
19982                     e.stopEvent();
19983                     this.win.focus();
19984                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19985                     this.deferFocus();
19986                 }
19987                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19988                     this.cleanUpPaste.defer(100, this);
19989                     return;
19990                 }
19991                 
19992             };
19993         }else if(Roo.isSafari){
19994             return function(e){
19995                 var k = e.getKey();
19996                 
19997                 if(k == e.TAB){
19998                     e.stopEvent();
19999                     this.execCmd('InsertText','\t');
20000                     this.deferFocus();
20001                     return;
20002                 }
20003                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20004                     this.cleanUpPaste.defer(100, this);
20005                     return;
20006                 }
20007                 
20008              };
20009         }
20010     }(),
20011     
20012     getAllAncestors: function()
20013     {
20014         var p = this.getSelectedNode();
20015         var a = [];
20016         if (!p) {
20017             a.push(p); // push blank onto stack..
20018             p = this.getParentElement();
20019         }
20020         
20021         
20022         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20023             a.push(p);
20024             p = p.parentNode;
20025         }
20026         a.push(this.doc.body);
20027         return a;
20028     },
20029     lastSel : false,
20030     lastSelNode : false,
20031     
20032     
20033     getSelection : function() 
20034     {
20035         this.assignDocWin();
20036         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20037     },
20038     
20039     getSelectedNode: function() 
20040     {
20041         // this may only work on Gecko!!!
20042         
20043         // should we cache this!!!!
20044         
20045         
20046         
20047          
20048         var range = this.createRange(this.getSelection()).cloneRange();
20049         
20050         if (Roo.isIE) {
20051             var parent = range.parentElement();
20052             while (true) {
20053                 var testRange = range.duplicate();
20054                 testRange.moveToElementText(parent);
20055                 if (testRange.inRange(range)) {
20056                     break;
20057                 }
20058                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20059                     break;
20060                 }
20061                 parent = parent.parentElement;
20062             }
20063             return parent;
20064         }
20065         
20066         // is ancestor a text element.
20067         var ac =  range.commonAncestorContainer;
20068         if (ac.nodeType == 3) {
20069             ac = ac.parentNode;
20070         }
20071         
20072         var ar = ac.childNodes;
20073          
20074         var nodes = [];
20075         var other_nodes = [];
20076         var has_other_nodes = false;
20077         for (var i=0;i<ar.length;i++) {
20078             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20079                 continue;
20080             }
20081             // fullly contained node.
20082             
20083             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20084                 nodes.push(ar[i]);
20085                 continue;
20086             }
20087             
20088             // probably selected..
20089             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20090                 other_nodes.push(ar[i]);
20091                 continue;
20092             }
20093             // outer..
20094             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20095                 continue;
20096             }
20097             
20098             
20099             has_other_nodes = true;
20100         }
20101         if (!nodes.length && other_nodes.length) {
20102             nodes= other_nodes;
20103         }
20104         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20105             return false;
20106         }
20107         
20108         return nodes[0];
20109     },
20110     createRange: function(sel)
20111     {
20112         // this has strange effects when using with 
20113         // top toolbar - not sure if it's a great idea.
20114         //this.editor.contentWindow.focus();
20115         if (typeof sel != "undefined") {
20116             try {
20117                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20118             } catch(e) {
20119                 return this.doc.createRange();
20120             }
20121         } else {
20122             return this.doc.createRange();
20123         }
20124     },
20125     getParentElement: function()
20126     {
20127         
20128         this.assignDocWin();
20129         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20130         
20131         var range = this.createRange(sel);
20132          
20133         try {
20134             var p = range.commonAncestorContainer;
20135             while (p.nodeType == 3) { // text node
20136                 p = p.parentNode;
20137             }
20138             return p;
20139         } catch (e) {
20140             return null;
20141         }
20142     
20143     },
20144     /***
20145      *
20146      * Range intersection.. the hard stuff...
20147      *  '-1' = before
20148      *  '0' = hits..
20149      *  '1' = after.
20150      *         [ -- selected range --- ]
20151      *   [fail]                        [fail]
20152      *
20153      *    basically..
20154      *      if end is before start or  hits it. fail.
20155      *      if start is after end or hits it fail.
20156      *
20157      *   if either hits (but other is outside. - then it's not 
20158      *   
20159      *    
20160      **/
20161     
20162     
20163     // @see http://www.thismuchiknow.co.uk/?p=64.
20164     rangeIntersectsNode : function(range, node)
20165     {
20166         var nodeRange = node.ownerDocument.createRange();
20167         try {
20168             nodeRange.selectNode(node);
20169         } catch (e) {
20170             nodeRange.selectNodeContents(node);
20171         }
20172     
20173         var rangeStartRange = range.cloneRange();
20174         rangeStartRange.collapse(true);
20175     
20176         var rangeEndRange = range.cloneRange();
20177         rangeEndRange.collapse(false);
20178     
20179         var nodeStartRange = nodeRange.cloneRange();
20180         nodeStartRange.collapse(true);
20181     
20182         var nodeEndRange = nodeRange.cloneRange();
20183         nodeEndRange.collapse(false);
20184     
20185         return rangeStartRange.compareBoundaryPoints(
20186                  Range.START_TO_START, nodeEndRange) == -1 &&
20187                rangeEndRange.compareBoundaryPoints(
20188                  Range.START_TO_START, nodeStartRange) == 1;
20189         
20190          
20191     },
20192     rangeCompareNode : function(range, node)
20193     {
20194         var nodeRange = node.ownerDocument.createRange();
20195         try {
20196             nodeRange.selectNode(node);
20197         } catch (e) {
20198             nodeRange.selectNodeContents(node);
20199         }
20200         
20201         
20202         range.collapse(true);
20203     
20204         nodeRange.collapse(true);
20205      
20206         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20207         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20208          
20209         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20210         
20211         var nodeIsBefore   =  ss == 1;
20212         var nodeIsAfter    = ee == -1;
20213         
20214         if (nodeIsBefore && nodeIsAfter) {
20215             return 0; // outer
20216         }
20217         if (!nodeIsBefore && nodeIsAfter) {
20218             return 1; //right trailed.
20219         }
20220         
20221         if (nodeIsBefore && !nodeIsAfter) {
20222             return 2;  // left trailed.
20223         }
20224         // fully contined.
20225         return 3;
20226     },
20227
20228     // private? - in a new class?
20229     cleanUpPaste :  function()
20230     {
20231         // cleans up the whole document..
20232         Roo.log('cleanuppaste');
20233         
20234         this.cleanUpChildren(this.doc.body);
20235         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20236         if (clean != this.doc.body.innerHTML) {
20237             this.doc.body.innerHTML = clean;
20238         }
20239         
20240     },
20241     
20242     cleanWordChars : function(input) {// change the chars to hex code
20243         var he = Roo.HtmlEditorCore;
20244         
20245         var output = input;
20246         Roo.each(he.swapCodes, function(sw) { 
20247             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20248             
20249             output = output.replace(swapper, sw[1]);
20250         });
20251         
20252         return output;
20253     },
20254     
20255     
20256     cleanUpChildren : function (n)
20257     {
20258         if (!n.childNodes.length) {
20259             return;
20260         }
20261         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20262            this.cleanUpChild(n.childNodes[i]);
20263         }
20264     },
20265     
20266     
20267         
20268     
20269     cleanUpChild : function (node)
20270     {
20271         var ed = this;
20272         //console.log(node);
20273         if (node.nodeName == "#text") {
20274             // clean up silly Windows -- stuff?
20275             return; 
20276         }
20277         if (node.nodeName == "#comment") {
20278             node.parentNode.removeChild(node);
20279             // clean up silly Windows -- stuff?
20280             return; 
20281         }
20282         var lcname = node.tagName.toLowerCase();
20283         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20284         // whitelist of tags..
20285         
20286         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20287             // remove node.
20288             node.parentNode.removeChild(node);
20289             return;
20290             
20291         }
20292         
20293         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20294         
20295         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20296         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20297         
20298         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20299         //    remove_keep_children = true;
20300         //}
20301         
20302         if (remove_keep_children) {
20303             this.cleanUpChildren(node);
20304             // inserts everything just before this node...
20305             while (node.childNodes.length) {
20306                 var cn = node.childNodes[0];
20307                 node.removeChild(cn);
20308                 node.parentNode.insertBefore(cn, node);
20309             }
20310             node.parentNode.removeChild(node);
20311             return;
20312         }
20313         
20314         if (!node.attributes || !node.attributes.length) {
20315             this.cleanUpChildren(node);
20316             return;
20317         }
20318         
20319         function cleanAttr(n,v)
20320         {
20321             
20322             if (v.match(/^\./) || v.match(/^\//)) {
20323                 return;
20324             }
20325             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20326                 return;
20327             }
20328             if (v.match(/^#/)) {
20329                 return;
20330             }
20331 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20332             node.removeAttribute(n);
20333             
20334         }
20335         
20336         var cwhite = this.cwhite;
20337         var cblack = this.cblack;
20338             
20339         function cleanStyle(n,v)
20340         {
20341             if (v.match(/expression/)) { //XSS?? should we even bother..
20342                 node.removeAttribute(n);
20343                 return;
20344             }
20345             
20346             var parts = v.split(/;/);
20347             var clean = [];
20348             
20349             Roo.each(parts, function(p) {
20350                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20351                 if (!p.length) {
20352                     return true;
20353                 }
20354                 var l = p.split(':').shift().replace(/\s+/g,'');
20355                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20356                 
20357                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20358 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20359                     //node.removeAttribute(n);
20360                     return true;
20361                 }
20362                 //Roo.log()
20363                 // only allow 'c whitelisted system attributes'
20364                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20365 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20366                     //node.removeAttribute(n);
20367                     return true;
20368                 }
20369                 
20370                 
20371                  
20372                 
20373                 clean.push(p);
20374                 return true;
20375             });
20376             if (clean.length) { 
20377                 node.setAttribute(n, clean.join(';'));
20378             } else {
20379                 node.removeAttribute(n);
20380             }
20381             
20382         }
20383         
20384         
20385         for (var i = node.attributes.length-1; i > -1 ; i--) {
20386             var a = node.attributes[i];
20387             //console.log(a);
20388             
20389             if (a.name.toLowerCase().substr(0,2)=='on')  {
20390                 node.removeAttribute(a.name);
20391                 continue;
20392             }
20393             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20394                 node.removeAttribute(a.name);
20395                 continue;
20396             }
20397             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20398                 cleanAttr(a.name,a.value); // fixme..
20399                 continue;
20400             }
20401             if (a.name == 'style') {
20402                 cleanStyle(a.name,a.value);
20403                 continue;
20404             }
20405             /// clean up MS crap..
20406             // tecnically this should be a list of valid class'es..
20407             
20408             
20409             if (a.name == 'class') {
20410                 if (a.value.match(/^Mso/)) {
20411                     node.className = '';
20412                 }
20413                 
20414                 if (a.value.match(/body/)) {
20415                     node.className = '';
20416                 }
20417                 continue;
20418             }
20419             
20420             // style cleanup!?
20421             // class cleanup?
20422             
20423         }
20424         
20425         
20426         this.cleanUpChildren(node);
20427         
20428         
20429     },
20430     
20431     /**
20432      * Clean up MS wordisms...
20433      */
20434     cleanWord : function(node)
20435     {
20436         
20437         
20438         if (!node) {
20439             this.cleanWord(this.doc.body);
20440             return;
20441         }
20442         if (node.nodeName == "#text") {
20443             // clean up silly Windows -- stuff?
20444             return; 
20445         }
20446         if (node.nodeName == "#comment") {
20447             node.parentNode.removeChild(node);
20448             // clean up silly Windows -- stuff?
20449             return; 
20450         }
20451         
20452         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20453             node.parentNode.removeChild(node);
20454             return;
20455         }
20456         
20457         // remove - but keep children..
20458         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20459             while (node.childNodes.length) {
20460                 var cn = node.childNodes[0];
20461                 node.removeChild(cn);
20462                 node.parentNode.insertBefore(cn, node);
20463             }
20464             node.parentNode.removeChild(node);
20465             this.iterateChildren(node, this.cleanWord);
20466             return;
20467         }
20468         // clean styles
20469         if (node.className.length) {
20470             
20471             var cn = node.className.split(/\W+/);
20472             var cna = [];
20473             Roo.each(cn, function(cls) {
20474                 if (cls.match(/Mso[a-zA-Z]+/)) {
20475                     return;
20476                 }
20477                 cna.push(cls);
20478             });
20479             node.className = cna.length ? cna.join(' ') : '';
20480             if (!cna.length) {
20481                 node.removeAttribute("class");
20482             }
20483         }
20484         
20485         if (node.hasAttribute("lang")) {
20486             node.removeAttribute("lang");
20487         }
20488         
20489         if (node.hasAttribute("style")) {
20490             
20491             var styles = node.getAttribute("style").split(";");
20492             var nstyle = [];
20493             Roo.each(styles, function(s) {
20494                 if (!s.match(/:/)) {
20495                     return;
20496                 }
20497                 var kv = s.split(":");
20498                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20499                     return;
20500                 }
20501                 // what ever is left... we allow.
20502                 nstyle.push(s);
20503             });
20504             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20505             if (!nstyle.length) {
20506                 node.removeAttribute('style');
20507             }
20508         }
20509         this.iterateChildren(node, this.cleanWord);
20510         
20511         
20512         
20513     },
20514     /**
20515      * iterateChildren of a Node, calling fn each time, using this as the scole..
20516      * @param {DomNode} node node to iterate children of.
20517      * @param {Function} fn method of this class to call on each item.
20518      */
20519     iterateChildren : function(node, fn)
20520     {
20521         if (!node.childNodes.length) {
20522                 return;
20523         }
20524         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20525            fn.call(this, node.childNodes[i])
20526         }
20527     },
20528     
20529     
20530     /**
20531      * cleanTableWidths.
20532      *
20533      * Quite often pasting from word etc.. results in tables with column and widths.
20534      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20535      *
20536      */
20537     cleanTableWidths : function(node)
20538     {
20539          
20540          
20541         if (!node) {
20542             this.cleanTableWidths(this.doc.body);
20543             return;
20544         }
20545         
20546         // ignore list...
20547         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20548             return; 
20549         }
20550         Roo.log(node.tagName);
20551         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20552             this.iterateChildren(node, this.cleanTableWidths);
20553             return;
20554         }
20555         if (node.hasAttribute('width')) {
20556             node.removeAttribute('width');
20557         }
20558         
20559          
20560         if (node.hasAttribute("style")) {
20561             // pretty basic...
20562             
20563             var styles = node.getAttribute("style").split(";");
20564             var nstyle = [];
20565             Roo.each(styles, function(s) {
20566                 if (!s.match(/:/)) {
20567                     return;
20568                 }
20569                 var kv = s.split(":");
20570                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20571                     return;
20572                 }
20573                 // what ever is left... we allow.
20574                 nstyle.push(s);
20575             });
20576             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20577             if (!nstyle.length) {
20578                 node.removeAttribute('style');
20579             }
20580         }
20581         
20582         this.iterateChildren(node, this.cleanTableWidths);
20583         
20584         
20585     },
20586     
20587     
20588     
20589     
20590     domToHTML : function(currentElement, depth, nopadtext) {
20591         
20592         depth = depth || 0;
20593         nopadtext = nopadtext || false;
20594     
20595         if (!currentElement) {
20596             return this.domToHTML(this.doc.body);
20597         }
20598         
20599         //Roo.log(currentElement);
20600         var j;
20601         var allText = false;
20602         var nodeName = currentElement.nodeName;
20603         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20604         
20605         if  (nodeName == '#text') {
20606             
20607             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20608         }
20609         
20610         
20611         var ret = '';
20612         if (nodeName != 'BODY') {
20613              
20614             var i = 0;
20615             // Prints the node tagName, such as <A>, <IMG>, etc
20616             if (tagName) {
20617                 var attr = [];
20618                 for(i = 0; i < currentElement.attributes.length;i++) {
20619                     // quoting?
20620                     var aname = currentElement.attributes.item(i).name;
20621                     if (!currentElement.attributes.item(i).value.length) {
20622                         continue;
20623                     }
20624                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20625                 }
20626                 
20627                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20628             } 
20629             else {
20630                 
20631                 // eack
20632             }
20633         } else {
20634             tagName = false;
20635         }
20636         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20637             return ret;
20638         }
20639         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20640             nopadtext = true;
20641         }
20642         
20643         
20644         // Traverse the tree
20645         i = 0;
20646         var currentElementChild = currentElement.childNodes.item(i);
20647         var allText = true;
20648         var innerHTML  = '';
20649         lastnode = '';
20650         while (currentElementChild) {
20651             // Formatting code (indent the tree so it looks nice on the screen)
20652             var nopad = nopadtext;
20653             if (lastnode == 'SPAN') {
20654                 nopad  = true;
20655             }
20656             // text
20657             if  (currentElementChild.nodeName == '#text') {
20658                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20659                 toadd = nopadtext ? toadd : toadd.trim();
20660                 if (!nopad && toadd.length > 80) {
20661                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20662                 }
20663                 innerHTML  += toadd;
20664                 
20665                 i++;
20666                 currentElementChild = currentElement.childNodes.item(i);
20667                 lastNode = '';
20668                 continue;
20669             }
20670             allText = false;
20671             
20672             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20673                 
20674             // Recursively traverse the tree structure of the child node
20675             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20676             lastnode = currentElementChild.nodeName;
20677             i++;
20678             currentElementChild=currentElement.childNodes.item(i);
20679         }
20680         
20681         ret += innerHTML;
20682         
20683         if (!allText) {
20684                 // The remaining code is mostly for formatting the tree
20685             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20686         }
20687         
20688         
20689         if (tagName) {
20690             ret+= "</"+tagName+">";
20691         }
20692         return ret;
20693         
20694     },
20695         
20696     applyBlacklists : function()
20697     {
20698         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20699         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20700         
20701         this.white = [];
20702         this.black = [];
20703         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20704             if (b.indexOf(tag) > -1) {
20705                 return;
20706             }
20707             this.white.push(tag);
20708             
20709         }, this);
20710         
20711         Roo.each(w, function(tag) {
20712             if (b.indexOf(tag) > -1) {
20713                 return;
20714             }
20715             if (this.white.indexOf(tag) > -1) {
20716                 return;
20717             }
20718             this.white.push(tag);
20719             
20720         }, this);
20721         
20722         
20723         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20724             if (w.indexOf(tag) > -1) {
20725                 return;
20726             }
20727             this.black.push(tag);
20728             
20729         }, this);
20730         
20731         Roo.each(b, function(tag) {
20732             if (w.indexOf(tag) > -1) {
20733                 return;
20734             }
20735             if (this.black.indexOf(tag) > -1) {
20736                 return;
20737             }
20738             this.black.push(tag);
20739             
20740         }, this);
20741         
20742         
20743         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20744         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20745         
20746         this.cwhite = [];
20747         this.cblack = [];
20748         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20749             if (b.indexOf(tag) > -1) {
20750                 return;
20751             }
20752             this.cwhite.push(tag);
20753             
20754         }, this);
20755         
20756         Roo.each(w, function(tag) {
20757             if (b.indexOf(tag) > -1) {
20758                 return;
20759             }
20760             if (this.cwhite.indexOf(tag) > -1) {
20761                 return;
20762             }
20763             this.cwhite.push(tag);
20764             
20765         }, this);
20766         
20767         
20768         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20769             if (w.indexOf(tag) > -1) {
20770                 return;
20771             }
20772             this.cblack.push(tag);
20773             
20774         }, this);
20775         
20776         Roo.each(b, function(tag) {
20777             if (w.indexOf(tag) > -1) {
20778                 return;
20779             }
20780             if (this.cblack.indexOf(tag) > -1) {
20781                 return;
20782             }
20783             this.cblack.push(tag);
20784             
20785         }, this);
20786     },
20787     
20788     setStylesheets : function(stylesheets)
20789     {
20790         if(typeof(stylesheets) == 'string'){
20791             Roo.get(this.iframe.contentDocument.head).createChild({
20792                 tag : 'link',
20793                 rel : 'stylesheet',
20794                 type : 'text/css',
20795                 href : stylesheets
20796             });
20797             
20798             return;
20799         }
20800         var _this = this;
20801      
20802         Roo.each(stylesheets, function(s) {
20803             if(!s.length){
20804                 return;
20805             }
20806             
20807             Roo.get(_this.iframe.contentDocument.head).createChild({
20808                 tag : 'link',
20809                 rel : 'stylesheet',
20810                 type : 'text/css',
20811                 href : s
20812             });
20813         });
20814
20815         
20816     },
20817     
20818     removeStylesheets : function()
20819     {
20820         var _this = this;
20821         
20822         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20823             s.remove();
20824         });
20825     }
20826     
20827     // hide stuff that is not compatible
20828     /**
20829      * @event blur
20830      * @hide
20831      */
20832     /**
20833      * @event change
20834      * @hide
20835      */
20836     /**
20837      * @event focus
20838      * @hide
20839      */
20840     /**
20841      * @event specialkey
20842      * @hide
20843      */
20844     /**
20845      * @cfg {String} fieldClass @hide
20846      */
20847     /**
20848      * @cfg {String} focusClass @hide
20849      */
20850     /**
20851      * @cfg {String} autoCreate @hide
20852      */
20853     /**
20854      * @cfg {String} inputType @hide
20855      */
20856     /**
20857      * @cfg {String} invalidClass @hide
20858      */
20859     /**
20860      * @cfg {String} invalidText @hide
20861      */
20862     /**
20863      * @cfg {String} msgFx @hide
20864      */
20865     /**
20866      * @cfg {String} validateOnBlur @hide
20867      */
20868 });
20869
20870 Roo.HtmlEditorCore.white = [
20871         'area', 'br', 'img', 'input', 'hr', 'wbr',
20872         
20873        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20874        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20875        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20876        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20877        'table',   'ul',         'xmp', 
20878        
20879        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20880       'thead',   'tr', 
20881      
20882       'dir', 'menu', 'ol', 'ul', 'dl',
20883        
20884       'embed',  'object'
20885 ];
20886
20887
20888 Roo.HtmlEditorCore.black = [
20889     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20890         'applet', // 
20891         'base',   'basefont', 'bgsound', 'blink',  'body', 
20892         'frame',  'frameset', 'head',    'html',   'ilayer', 
20893         'iframe', 'layer',  'link',     'meta',    'object',   
20894         'script', 'style' ,'title',  'xml' // clean later..
20895 ];
20896 Roo.HtmlEditorCore.clean = [
20897     'script', 'style', 'title', 'xml'
20898 ];
20899 Roo.HtmlEditorCore.remove = [
20900     'font'
20901 ];
20902 // attributes..
20903
20904 Roo.HtmlEditorCore.ablack = [
20905     'on'
20906 ];
20907     
20908 Roo.HtmlEditorCore.aclean = [ 
20909     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20910 ];
20911
20912 // protocols..
20913 Roo.HtmlEditorCore.pwhite= [
20914         'http',  'https',  'mailto'
20915 ];
20916
20917 // white listed style attributes.
20918 Roo.HtmlEditorCore.cwhite= [
20919       //  'text-align', /// default is to allow most things..
20920       
20921          
20922 //        'font-size'//??
20923 ];
20924
20925 // black listed style attributes.
20926 Roo.HtmlEditorCore.cblack= [
20927       //  'font-size' -- this can be set by the project 
20928 ];
20929
20930
20931 Roo.HtmlEditorCore.swapCodes   =[ 
20932     [    8211, "--" ], 
20933     [    8212, "--" ], 
20934     [    8216,  "'" ],  
20935     [    8217, "'" ],  
20936     [    8220, '"' ],  
20937     [    8221, '"' ],  
20938     [    8226, "*" ],  
20939     [    8230, "..." ]
20940 ]; 
20941
20942     /*
20943  * - LGPL
20944  *
20945  * HtmlEditor
20946  * 
20947  */
20948
20949 /**
20950  * @class Roo.bootstrap.HtmlEditor
20951  * @extends Roo.bootstrap.TextArea
20952  * Bootstrap HtmlEditor class
20953
20954  * @constructor
20955  * Create a new HtmlEditor
20956  * @param {Object} config The config object
20957  */
20958
20959 Roo.bootstrap.HtmlEditor = function(config){
20960     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20961     if (!this.toolbars) {
20962         this.toolbars = [];
20963     }
20964     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20965     this.addEvents({
20966             /**
20967              * @event initialize
20968              * Fires when the editor is fully initialized (including the iframe)
20969              * @param {HtmlEditor} this
20970              */
20971             initialize: true,
20972             /**
20973              * @event activate
20974              * Fires when the editor is first receives the focus. Any insertion must wait
20975              * until after this event.
20976              * @param {HtmlEditor} this
20977              */
20978             activate: true,
20979              /**
20980              * @event beforesync
20981              * Fires before the textarea is updated with content from the editor iframe. Return false
20982              * to cancel the sync.
20983              * @param {HtmlEditor} this
20984              * @param {String} html
20985              */
20986             beforesync: true,
20987              /**
20988              * @event beforepush
20989              * Fires before the iframe editor is updated with content from the textarea. Return false
20990              * to cancel the push.
20991              * @param {HtmlEditor} this
20992              * @param {String} html
20993              */
20994             beforepush: true,
20995              /**
20996              * @event sync
20997              * Fires when the textarea is updated with content from the editor iframe.
20998              * @param {HtmlEditor} this
20999              * @param {String} html
21000              */
21001             sync: true,
21002              /**
21003              * @event push
21004              * Fires when the iframe editor is updated with content from the textarea.
21005              * @param {HtmlEditor} this
21006              * @param {String} html
21007              */
21008             push: true,
21009              /**
21010              * @event editmodechange
21011              * Fires when the editor switches edit modes
21012              * @param {HtmlEditor} this
21013              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21014              */
21015             editmodechange: true,
21016             /**
21017              * @event editorevent
21018              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21019              * @param {HtmlEditor} this
21020              */
21021             editorevent: true,
21022             /**
21023              * @event firstfocus
21024              * Fires when on first focus - needed by toolbars..
21025              * @param {HtmlEditor} this
21026              */
21027             firstfocus: true,
21028             /**
21029              * @event autosave
21030              * Auto save the htmlEditor value as a file into Events
21031              * @param {HtmlEditor} this
21032              */
21033             autosave: true,
21034             /**
21035              * @event savedpreview
21036              * preview the saved version of htmlEditor
21037              * @param {HtmlEditor} this
21038              */
21039             savedpreview: true
21040         });
21041 };
21042
21043
21044 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21045     
21046     
21047       /**
21048      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21049      */
21050     toolbars : false,
21051    
21052      /**
21053      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21054      *                        Roo.resizable.
21055      */
21056     resizable : false,
21057      /**
21058      * @cfg {Number} height (in pixels)
21059      */   
21060     height: 300,
21061    /**
21062      * @cfg {Number} width (in pixels)
21063      */   
21064     width: false,
21065     
21066     /**
21067      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21068      * 
21069      */
21070     stylesheets: false,
21071     
21072     // id of frame..
21073     frameId: false,
21074     
21075     // private properties
21076     validationEvent : false,
21077     deferHeight: true,
21078     initialized : false,
21079     activated : false,
21080     
21081     onFocus : Roo.emptyFn,
21082     iframePad:3,
21083     hideMode:'offsets',
21084     
21085     
21086     tbContainer : false,
21087     
21088     toolbarContainer :function() {
21089         return this.wrap.select('.x-html-editor-tb',true).first();
21090     },
21091
21092     /**
21093      * Protected method that will not generally be called directly. It
21094      * is called when the editor creates its toolbar. Override this method if you need to
21095      * add custom toolbar buttons.
21096      * @param {HtmlEditor} editor
21097      */
21098     createToolbar : function(){
21099         
21100         Roo.log("create toolbars");
21101         
21102         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21103         this.toolbars[0].render(this.toolbarContainer());
21104         
21105         return;
21106         
21107 //        if (!editor.toolbars || !editor.toolbars.length) {
21108 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21109 //        }
21110 //        
21111 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21112 //            editor.toolbars[i] = Roo.factory(
21113 //                    typeof(editor.toolbars[i]) == 'string' ?
21114 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21115 //                Roo.bootstrap.HtmlEditor);
21116 //            editor.toolbars[i].init(editor);
21117 //        }
21118     },
21119
21120      
21121     // private
21122     onRender : function(ct, position)
21123     {
21124        // Roo.log("Call onRender: " + this.xtype);
21125         var _t = this;
21126         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21127       
21128         this.wrap = this.inputEl().wrap({
21129             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21130         });
21131         
21132         this.editorcore.onRender(ct, position);
21133          
21134         if (this.resizable) {
21135             this.resizeEl = new Roo.Resizable(this.wrap, {
21136                 pinned : true,
21137                 wrap: true,
21138                 dynamic : true,
21139                 minHeight : this.height,
21140                 height: this.height,
21141                 handles : this.resizable,
21142                 width: this.width,
21143                 listeners : {
21144                     resize : function(r, w, h) {
21145                         _t.onResize(w,h); // -something
21146                     }
21147                 }
21148             });
21149             
21150         }
21151         this.createToolbar(this);
21152        
21153         
21154         if(!this.width && this.resizable){
21155             this.setSize(this.wrap.getSize());
21156         }
21157         if (this.resizeEl) {
21158             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21159             // should trigger onReize..
21160         }
21161         
21162     },
21163
21164     // private
21165     onResize : function(w, h)
21166     {
21167         Roo.log('resize: ' +w + ',' + h );
21168         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21169         var ew = false;
21170         var eh = false;
21171         
21172         if(this.inputEl() ){
21173             if(typeof w == 'number'){
21174                 var aw = w - this.wrap.getFrameWidth('lr');
21175                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21176                 ew = aw;
21177             }
21178             if(typeof h == 'number'){
21179                  var tbh = -11;  // fixme it needs to tool bar size!
21180                 for (var i =0; i < this.toolbars.length;i++) {
21181                     // fixme - ask toolbars for heights?
21182                     tbh += this.toolbars[i].el.getHeight();
21183                     //if (this.toolbars[i].footer) {
21184                     //    tbh += this.toolbars[i].footer.el.getHeight();
21185                     //}
21186                 }
21187               
21188                 
21189                 
21190                 
21191                 
21192                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21193                 ah -= 5; // knock a few pixes off for look..
21194                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21195                 var eh = ah;
21196             }
21197         }
21198         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21199         this.editorcore.onResize(ew,eh);
21200         
21201     },
21202
21203     /**
21204      * Toggles the editor between standard and source edit mode.
21205      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21206      */
21207     toggleSourceEdit : function(sourceEditMode)
21208     {
21209         this.editorcore.toggleSourceEdit(sourceEditMode);
21210         
21211         if(this.editorcore.sourceEditMode){
21212             Roo.log('editor - showing textarea');
21213             
21214 //            Roo.log('in');
21215 //            Roo.log(this.syncValue());
21216             this.syncValue();
21217             this.inputEl().removeClass(['hide', 'x-hidden']);
21218             this.inputEl().dom.removeAttribute('tabIndex');
21219             this.inputEl().focus();
21220         }else{
21221             Roo.log('editor - hiding textarea');
21222 //            Roo.log('out')
21223 //            Roo.log(this.pushValue()); 
21224             this.pushValue();
21225             
21226             this.inputEl().addClass(['hide', 'x-hidden']);
21227             this.inputEl().dom.setAttribute('tabIndex', -1);
21228             //this.deferFocus();
21229         }
21230          
21231         if(this.resizable){
21232             this.setSize(this.wrap.getSize());
21233         }
21234         
21235         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21236     },
21237  
21238     // private (for BoxComponent)
21239     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21240
21241     // private (for BoxComponent)
21242     getResizeEl : function(){
21243         return this.wrap;
21244     },
21245
21246     // private (for BoxComponent)
21247     getPositionEl : function(){
21248         return this.wrap;
21249     },
21250
21251     // private
21252     initEvents : function(){
21253         this.originalValue = this.getValue();
21254     },
21255
21256 //    /**
21257 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21258 //     * @method
21259 //     */
21260 //    markInvalid : Roo.emptyFn,
21261 //    /**
21262 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21263 //     * @method
21264 //     */
21265 //    clearInvalid : Roo.emptyFn,
21266
21267     setValue : function(v){
21268         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21269         this.editorcore.pushValue();
21270     },
21271
21272      
21273     // private
21274     deferFocus : function(){
21275         this.focus.defer(10, this);
21276     },
21277
21278     // doc'ed in Field
21279     focus : function(){
21280         this.editorcore.focus();
21281         
21282     },
21283       
21284
21285     // private
21286     onDestroy : function(){
21287         
21288         
21289         
21290         if(this.rendered){
21291             
21292             for (var i =0; i < this.toolbars.length;i++) {
21293                 // fixme - ask toolbars for heights?
21294                 this.toolbars[i].onDestroy();
21295             }
21296             
21297             this.wrap.dom.innerHTML = '';
21298             this.wrap.remove();
21299         }
21300     },
21301
21302     // private
21303     onFirstFocus : function(){
21304         //Roo.log("onFirstFocus");
21305         this.editorcore.onFirstFocus();
21306          for (var i =0; i < this.toolbars.length;i++) {
21307             this.toolbars[i].onFirstFocus();
21308         }
21309         
21310     },
21311     
21312     // private
21313     syncValue : function()
21314     {   
21315         this.editorcore.syncValue();
21316     },
21317     
21318     pushValue : function()
21319     {   
21320         this.editorcore.pushValue();
21321     }
21322      
21323     
21324     // hide stuff that is not compatible
21325     /**
21326      * @event blur
21327      * @hide
21328      */
21329     /**
21330      * @event change
21331      * @hide
21332      */
21333     /**
21334      * @event focus
21335      * @hide
21336      */
21337     /**
21338      * @event specialkey
21339      * @hide
21340      */
21341     /**
21342      * @cfg {String} fieldClass @hide
21343      */
21344     /**
21345      * @cfg {String} focusClass @hide
21346      */
21347     /**
21348      * @cfg {String} autoCreate @hide
21349      */
21350     /**
21351      * @cfg {String} inputType @hide
21352      */
21353     /**
21354      * @cfg {String} invalidClass @hide
21355      */
21356     /**
21357      * @cfg {String} invalidText @hide
21358      */
21359     /**
21360      * @cfg {String} msgFx @hide
21361      */
21362     /**
21363      * @cfg {String} validateOnBlur @hide
21364      */
21365 });
21366  
21367     
21368    
21369    
21370    
21371       
21372 Roo.namespace('Roo.bootstrap.htmleditor');
21373 /**
21374  * @class Roo.bootstrap.HtmlEditorToolbar1
21375  * Basic Toolbar
21376  * 
21377  * Usage:
21378  *
21379  new Roo.bootstrap.HtmlEditor({
21380     ....
21381     toolbars : [
21382         new Roo.bootstrap.HtmlEditorToolbar1({
21383             disable : { fonts: 1 , format: 1, ..., ... , ...],
21384             btns : [ .... ]
21385         })
21386     }
21387      
21388  * 
21389  * @cfg {Object} disable List of elements to disable..
21390  * @cfg {Array} btns List of additional buttons.
21391  * 
21392  * 
21393  * NEEDS Extra CSS? 
21394  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21395  */
21396  
21397 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21398 {
21399     
21400     Roo.apply(this, config);
21401     
21402     // default disabled, based on 'good practice'..
21403     this.disable = this.disable || {};
21404     Roo.applyIf(this.disable, {
21405         fontSize : true,
21406         colors : true,
21407         specialElements : true
21408     });
21409     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21410     
21411     this.editor = config.editor;
21412     this.editorcore = config.editor.editorcore;
21413     
21414     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21415     
21416     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21417     // dont call parent... till later.
21418 }
21419 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21420      
21421     bar : true,
21422     
21423     editor : false,
21424     editorcore : false,
21425     
21426     
21427     formats : [
21428         "p" ,  
21429         "h1","h2","h3","h4","h5","h6", 
21430         "pre", "code", 
21431         "abbr", "acronym", "address", "cite", "samp", "var",
21432         'div','span'
21433     ],
21434     
21435     onRender : function(ct, position)
21436     {
21437        // Roo.log("Call onRender: " + this.xtype);
21438         
21439        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21440        Roo.log(this.el);
21441        this.el.dom.style.marginBottom = '0';
21442        var _this = this;
21443        var editorcore = this.editorcore;
21444        var editor= this.editor;
21445        
21446        var children = [];
21447        var btn = function(id,cmd , toggle, handler){
21448        
21449             var  event = toggle ? 'toggle' : 'click';
21450        
21451             var a = {
21452                 size : 'sm',
21453                 xtype: 'Button',
21454                 xns: Roo.bootstrap,
21455                 glyphicon : id,
21456                 cmd : id || cmd,
21457                 enableToggle:toggle !== false,
21458                 //html : 'submit'
21459                 pressed : toggle ? false : null,
21460                 listeners : {}
21461             };
21462             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21463                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21464             };
21465             children.push(a);
21466             return a;
21467        }
21468         
21469         var style = {
21470                 xtype: 'Button',
21471                 size : 'sm',
21472                 xns: Roo.bootstrap,
21473                 glyphicon : 'font',
21474                 //html : 'submit'
21475                 menu : {
21476                     xtype: 'Menu',
21477                     xns: Roo.bootstrap,
21478                     items:  []
21479                 }
21480         };
21481         Roo.each(this.formats, function(f) {
21482             style.menu.items.push({
21483                 xtype :'MenuItem',
21484                 xns: Roo.bootstrap,
21485                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21486                 tagname : f,
21487                 listeners : {
21488                     click : function()
21489                     {
21490                         editorcore.insertTag(this.tagname);
21491                         editor.focus();
21492                     }
21493                 }
21494                 
21495             });
21496         });
21497          children.push(style);   
21498             
21499             
21500         btn('bold',false,true);
21501         btn('italic',false,true);
21502         btn('align-left', 'justifyleft',true);
21503         btn('align-center', 'justifycenter',true);
21504         btn('align-right' , 'justifyright',true);
21505         btn('link', false, false, function(btn) {
21506             //Roo.log("create link?");
21507             var url = prompt(this.createLinkText, this.defaultLinkValue);
21508             if(url && url != 'http:/'+'/'){
21509                 this.editorcore.relayCmd('createlink', url);
21510             }
21511         }),
21512         btn('list','insertunorderedlist',true);
21513         btn('pencil', false,true, function(btn){
21514                 Roo.log(this);
21515                 
21516                 this.toggleSourceEdit(btn.pressed);
21517         });
21518         /*
21519         var cog = {
21520                 xtype: 'Button',
21521                 size : 'sm',
21522                 xns: Roo.bootstrap,
21523                 glyphicon : 'cog',
21524                 //html : 'submit'
21525                 menu : {
21526                     xtype: 'Menu',
21527                     xns: Roo.bootstrap,
21528                     items:  []
21529                 }
21530         };
21531         
21532         cog.menu.items.push({
21533             xtype :'MenuItem',
21534             xns: Roo.bootstrap,
21535             html : Clean styles,
21536             tagname : f,
21537             listeners : {
21538                 click : function()
21539                 {
21540                     editorcore.insertTag(this.tagname);
21541                     editor.focus();
21542                 }
21543             }
21544             
21545         });
21546        */
21547         
21548          
21549        this.xtype = 'NavSimplebar';
21550         
21551         for(var i=0;i< children.length;i++) {
21552             
21553             this.buttons.add(this.addxtypeChild(children[i]));
21554             
21555         }
21556         
21557         editor.on('editorevent', this.updateToolbar, this);
21558     },
21559     onBtnClick : function(id)
21560     {
21561        this.editorcore.relayCmd(id);
21562        this.editorcore.focus();
21563     },
21564     
21565     /**
21566      * Protected method that will not generally be called directly. It triggers
21567      * a toolbar update by reading the markup state of the current selection in the editor.
21568      */
21569     updateToolbar: function(){
21570
21571         if(!this.editorcore.activated){
21572             this.editor.onFirstFocus(); // is this neeed?
21573             return;
21574         }
21575
21576         var btns = this.buttons; 
21577         var doc = this.editorcore.doc;
21578         btns.get('bold').setActive(doc.queryCommandState('bold'));
21579         btns.get('italic').setActive(doc.queryCommandState('italic'));
21580         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21581         
21582         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21583         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21584         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21585         
21586         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21587         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21588          /*
21589         
21590         var ans = this.editorcore.getAllAncestors();
21591         if (this.formatCombo) {
21592             
21593             
21594             var store = this.formatCombo.store;
21595             this.formatCombo.setValue("");
21596             for (var i =0; i < ans.length;i++) {
21597                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21598                     // select it..
21599                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21600                     break;
21601                 }
21602             }
21603         }
21604         
21605         
21606         
21607         // hides menus... - so this cant be on a menu...
21608         Roo.bootstrap.MenuMgr.hideAll();
21609         */
21610         Roo.bootstrap.MenuMgr.hideAll();
21611         //this.editorsyncValue();
21612     },
21613     onFirstFocus: function() {
21614         this.buttons.each(function(item){
21615            item.enable();
21616         });
21617     },
21618     toggleSourceEdit : function(sourceEditMode){
21619         
21620           
21621         if(sourceEditMode){
21622             Roo.log("disabling buttons");
21623            this.buttons.each( function(item){
21624                 if(item.cmd != 'pencil'){
21625                     item.disable();
21626                 }
21627             });
21628           
21629         }else{
21630             Roo.log("enabling buttons");
21631             if(this.editorcore.initialized){
21632                 this.buttons.each( function(item){
21633                     item.enable();
21634                 });
21635             }
21636             
21637         }
21638         Roo.log("calling toggole on editor");
21639         // tell the editor that it's been pressed..
21640         this.editor.toggleSourceEdit(sourceEditMode);
21641        
21642     }
21643 });
21644
21645
21646
21647
21648
21649 /**
21650  * @class Roo.bootstrap.Table.AbstractSelectionModel
21651  * @extends Roo.util.Observable
21652  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21653  * implemented by descendant classes.  This class should not be directly instantiated.
21654  * @constructor
21655  */
21656 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21657     this.locked = false;
21658     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21659 };
21660
21661
21662 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21663     /** @ignore Called by the grid automatically. Do not call directly. */
21664     init : function(grid){
21665         this.grid = grid;
21666         this.initEvents();
21667     },
21668
21669     /**
21670      * Locks the selections.
21671      */
21672     lock : function(){
21673         this.locked = true;
21674     },
21675
21676     /**
21677      * Unlocks the selections.
21678      */
21679     unlock : function(){
21680         this.locked = false;
21681     },
21682
21683     /**
21684      * Returns true if the selections are locked.
21685      * @return {Boolean}
21686      */
21687     isLocked : function(){
21688         return this.locked;
21689     }
21690 });
21691 /**
21692  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21693  * @class Roo.bootstrap.Table.RowSelectionModel
21694  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21695  * It supports multiple selections and keyboard selection/navigation. 
21696  * @constructor
21697  * @param {Object} config
21698  */
21699
21700 Roo.bootstrap.Table.RowSelectionModel = function(config){
21701     Roo.apply(this, config);
21702     this.selections = new Roo.util.MixedCollection(false, function(o){
21703         return o.id;
21704     });
21705
21706     this.last = false;
21707     this.lastActive = false;
21708
21709     this.addEvents({
21710         /**
21711              * @event selectionchange
21712              * Fires when the selection changes
21713              * @param {SelectionModel} this
21714              */
21715             "selectionchange" : true,
21716         /**
21717              * @event afterselectionchange
21718              * Fires after the selection changes (eg. by key press or clicking)
21719              * @param {SelectionModel} this
21720              */
21721             "afterselectionchange" : true,
21722         /**
21723              * @event beforerowselect
21724              * Fires when a row is selected being selected, return false to cancel.
21725              * @param {SelectionModel} this
21726              * @param {Number} rowIndex The selected index
21727              * @param {Boolean} keepExisting False if other selections will be cleared
21728              */
21729             "beforerowselect" : true,
21730         /**
21731              * @event rowselect
21732              * Fires when a row is selected.
21733              * @param {SelectionModel} this
21734              * @param {Number} rowIndex The selected index
21735              * @param {Roo.data.Record} r The record
21736              */
21737             "rowselect" : true,
21738         /**
21739              * @event rowdeselect
21740              * Fires when a row is deselected.
21741              * @param {SelectionModel} this
21742              * @param {Number} rowIndex The selected index
21743              */
21744         "rowdeselect" : true
21745     });
21746     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21747     this.locked = false;
21748 };
21749
21750 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21751     /**
21752      * @cfg {Boolean} singleSelect
21753      * True to allow selection of only one row at a time (defaults to false)
21754      */
21755     singleSelect : false,
21756
21757     // private
21758     initEvents : function(){
21759
21760         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21761             this.grid.on("mousedown", this.handleMouseDown, this);
21762         }else{ // allow click to work like normal
21763             this.grid.on("rowclick", this.handleDragableRowClick, this);
21764         }
21765
21766         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21767             "up" : function(e){
21768                 if(!e.shiftKey){
21769                     this.selectPrevious(e.shiftKey);
21770                 }else if(this.last !== false && this.lastActive !== false){
21771                     var last = this.last;
21772                     this.selectRange(this.last,  this.lastActive-1);
21773                     this.grid.getView().focusRow(this.lastActive);
21774                     if(last !== false){
21775                         this.last = last;
21776                     }
21777                 }else{
21778                     this.selectFirstRow();
21779                 }
21780                 this.fireEvent("afterselectionchange", this);
21781             },
21782             "down" : function(e){
21783                 if(!e.shiftKey){
21784                     this.selectNext(e.shiftKey);
21785                 }else if(this.last !== false && this.lastActive !== false){
21786                     var last = this.last;
21787                     this.selectRange(this.last,  this.lastActive+1);
21788                     this.grid.getView().focusRow(this.lastActive);
21789                     if(last !== false){
21790                         this.last = last;
21791                     }
21792                 }else{
21793                     this.selectFirstRow();
21794                 }
21795                 this.fireEvent("afterselectionchange", this);
21796             },
21797             scope: this
21798         });
21799
21800         var view = this.grid.view;
21801         view.on("refresh", this.onRefresh, this);
21802         view.on("rowupdated", this.onRowUpdated, this);
21803         view.on("rowremoved", this.onRemove, this);
21804     },
21805
21806     // private
21807     onRefresh : function(){
21808         var ds = this.grid.dataSource, i, v = this.grid.view;
21809         var s = this.selections;
21810         s.each(function(r){
21811             if((i = ds.indexOfId(r.id)) != -1){
21812                 v.onRowSelect(i);
21813             }else{
21814                 s.remove(r);
21815             }
21816         });
21817     },
21818
21819     // private
21820     onRemove : function(v, index, r){
21821         this.selections.remove(r);
21822     },
21823
21824     // private
21825     onRowUpdated : function(v, index, r){
21826         if(this.isSelected(r)){
21827             v.onRowSelect(index);
21828         }
21829     },
21830
21831     /**
21832      * Select records.
21833      * @param {Array} records The records to select
21834      * @param {Boolean} keepExisting (optional) True to keep existing selections
21835      */
21836     selectRecords : function(records, keepExisting){
21837         if(!keepExisting){
21838             this.clearSelections();
21839         }
21840         var ds = this.grid.dataSource;
21841         for(var i = 0, len = records.length; i < len; i++){
21842             this.selectRow(ds.indexOf(records[i]), true);
21843         }
21844     },
21845
21846     /**
21847      * Gets the number of selected rows.
21848      * @return {Number}
21849      */
21850     getCount : function(){
21851         return this.selections.length;
21852     },
21853
21854     /**
21855      * Selects the first row in the grid.
21856      */
21857     selectFirstRow : function(){
21858         this.selectRow(0);
21859     },
21860
21861     /**
21862      * Select the last row.
21863      * @param {Boolean} keepExisting (optional) True to keep existing selections
21864      */
21865     selectLastRow : function(keepExisting){
21866         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21867     },
21868
21869     /**
21870      * Selects the row immediately following the last selected row.
21871      * @param {Boolean} keepExisting (optional) True to keep existing selections
21872      */
21873     selectNext : function(keepExisting){
21874         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21875             this.selectRow(this.last+1, keepExisting);
21876             this.grid.getView().focusRow(this.last);
21877         }
21878     },
21879
21880     /**
21881      * Selects the row that precedes the last selected row.
21882      * @param {Boolean} keepExisting (optional) True to keep existing selections
21883      */
21884     selectPrevious : function(keepExisting){
21885         if(this.last){
21886             this.selectRow(this.last-1, keepExisting);
21887             this.grid.getView().focusRow(this.last);
21888         }
21889     },
21890
21891     /**
21892      * Returns the selected records
21893      * @return {Array} Array of selected records
21894      */
21895     getSelections : function(){
21896         return [].concat(this.selections.items);
21897     },
21898
21899     /**
21900      * Returns the first selected record.
21901      * @return {Record}
21902      */
21903     getSelected : function(){
21904         return this.selections.itemAt(0);
21905     },
21906
21907
21908     /**
21909      * Clears all selections.
21910      */
21911     clearSelections : function(fast){
21912         if(this.locked) {
21913             return;
21914         }
21915         if(fast !== true){
21916             var ds = this.grid.dataSource;
21917             var s = this.selections;
21918             s.each(function(r){
21919                 this.deselectRow(ds.indexOfId(r.id));
21920             }, this);
21921             s.clear();
21922         }else{
21923             this.selections.clear();
21924         }
21925         this.last = false;
21926     },
21927
21928
21929     /**
21930      * Selects all rows.
21931      */
21932     selectAll : function(){
21933         if(this.locked) {
21934             return;
21935         }
21936         this.selections.clear();
21937         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21938             this.selectRow(i, true);
21939         }
21940     },
21941
21942     /**
21943      * Returns True if there is a selection.
21944      * @return {Boolean}
21945      */
21946     hasSelection : function(){
21947         return this.selections.length > 0;
21948     },
21949
21950     /**
21951      * Returns True if the specified row is selected.
21952      * @param {Number/Record} record The record or index of the record to check
21953      * @return {Boolean}
21954      */
21955     isSelected : function(index){
21956         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21957         return (r && this.selections.key(r.id) ? true : false);
21958     },
21959
21960     /**
21961      * Returns True if the specified record id is selected.
21962      * @param {String} id The id of record to check
21963      * @return {Boolean}
21964      */
21965     isIdSelected : function(id){
21966         return (this.selections.key(id) ? true : false);
21967     },
21968
21969     // private
21970     handleMouseDown : function(e, t){
21971         var view = this.grid.getView(), rowIndex;
21972         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21973             return;
21974         };
21975         if(e.shiftKey && this.last !== false){
21976             var last = this.last;
21977             this.selectRange(last, rowIndex, e.ctrlKey);
21978             this.last = last; // reset the last
21979             view.focusRow(rowIndex);
21980         }else{
21981             var isSelected = this.isSelected(rowIndex);
21982             if(e.button !== 0 && isSelected){
21983                 view.focusRow(rowIndex);
21984             }else if(e.ctrlKey && isSelected){
21985                 this.deselectRow(rowIndex);
21986             }else if(!isSelected){
21987                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21988                 view.focusRow(rowIndex);
21989             }
21990         }
21991         this.fireEvent("afterselectionchange", this);
21992     },
21993     // private
21994     handleDragableRowClick :  function(grid, rowIndex, e) 
21995     {
21996         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21997             this.selectRow(rowIndex, false);
21998             grid.view.focusRow(rowIndex);
21999              this.fireEvent("afterselectionchange", this);
22000         }
22001     },
22002     
22003     /**
22004      * Selects multiple rows.
22005      * @param {Array} rows Array of the indexes of the row to select
22006      * @param {Boolean} keepExisting (optional) True to keep existing selections
22007      */
22008     selectRows : function(rows, keepExisting){
22009         if(!keepExisting){
22010             this.clearSelections();
22011         }
22012         for(var i = 0, len = rows.length; i < len; i++){
22013             this.selectRow(rows[i], true);
22014         }
22015     },
22016
22017     /**
22018      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22019      * @param {Number} startRow The index of the first row in the range
22020      * @param {Number} endRow The index of the last row in the range
22021      * @param {Boolean} keepExisting (optional) True to retain existing selections
22022      */
22023     selectRange : function(startRow, endRow, keepExisting){
22024         if(this.locked) {
22025             return;
22026         }
22027         if(!keepExisting){
22028             this.clearSelections();
22029         }
22030         if(startRow <= endRow){
22031             for(var i = startRow; i <= endRow; i++){
22032                 this.selectRow(i, true);
22033             }
22034         }else{
22035             for(var i = startRow; i >= endRow; i--){
22036                 this.selectRow(i, true);
22037             }
22038         }
22039     },
22040
22041     /**
22042      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22043      * @param {Number} startRow The index of the first row in the range
22044      * @param {Number} endRow The index of the last row in the range
22045      */
22046     deselectRange : function(startRow, endRow, preventViewNotify){
22047         if(this.locked) {
22048             return;
22049         }
22050         for(var i = startRow; i <= endRow; i++){
22051             this.deselectRow(i, preventViewNotify);
22052         }
22053     },
22054
22055     /**
22056      * Selects a row.
22057      * @param {Number} row The index of the row to select
22058      * @param {Boolean} keepExisting (optional) True to keep existing selections
22059      */
22060     selectRow : function(index, keepExisting, preventViewNotify){
22061         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22062             return;
22063         }
22064         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22065             if(!keepExisting || this.singleSelect){
22066                 this.clearSelections();
22067             }
22068             var r = this.grid.dataSource.getAt(index);
22069             this.selections.add(r);
22070             this.last = this.lastActive = index;
22071             if(!preventViewNotify){
22072                 this.grid.getView().onRowSelect(index);
22073             }
22074             this.fireEvent("rowselect", this, index, r);
22075             this.fireEvent("selectionchange", this);
22076         }
22077     },
22078
22079     /**
22080      * Deselects a row.
22081      * @param {Number} row The index of the row to deselect
22082      */
22083     deselectRow : function(index, preventViewNotify){
22084         if(this.locked) {
22085             return;
22086         }
22087         if(this.last == index){
22088             this.last = false;
22089         }
22090         if(this.lastActive == index){
22091             this.lastActive = false;
22092         }
22093         var r = this.grid.dataSource.getAt(index);
22094         this.selections.remove(r);
22095         if(!preventViewNotify){
22096             this.grid.getView().onRowDeselect(index);
22097         }
22098         this.fireEvent("rowdeselect", this, index);
22099         this.fireEvent("selectionchange", this);
22100     },
22101
22102     // private
22103     restoreLast : function(){
22104         if(this._last){
22105             this.last = this._last;
22106         }
22107     },
22108
22109     // private
22110     acceptsNav : function(row, col, cm){
22111         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22112     },
22113
22114     // private
22115     onEditorKey : function(field, e){
22116         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22117         if(k == e.TAB){
22118             e.stopEvent();
22119             ed.completeEdit();
22120             if(e.shiftKey){
22121                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22122             }else{
22123                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22124             }
22125         }else if(k == e.ENTER && !e.ctrlKey){
22126             e.stopEvent();
22127             ed.completeEdit();
22128             if(e.shiftKey){
22129                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22130             }else{
22131                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22132             }
22133         }else if(k == e.ESC){
22134             ed.cancelEdit();
22135         }
22136         if(newCell){
22137             g.startEditing(newCell[0], newCell[1]);
22138         }
22139     }
22140 });/*
22141  * Based on:
22142  * Ext JS Library 1.1.1
22143  * Copyright(c) 2006-2007, Ext JS, LLC.
22144  *
22145  * Originally Released Under LGPL - original licence link has changed is not relivant.
22146  *
22147  * Fork - LGPL
22148  * <script type="text/javascript">
22149  */
22150  
22151 /**
22152  * @class Roo.bootstrap.PagingToolbar
22153  * @extends Roo.bootstrap.NavSimplebar
22154  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22155  * @constructor
22156  * Create a new PagingToolbar
22157  * @param {Object} config The config object
22158  * @param {Roo.data.Store} store
22159  */
22160 Roo.bootstrap.PagingToolbar = function(config)
22161 {
22162     // old args format still supported... - xtype is prefered..
22163         // created from xtype...
22164     
22165     this.ds = config.dataSource;
22166     
22167     if (config.store && !this.ds) {
22168         this.store= Roo.factory(config.store, Roo.data);
22169         this.ds = this.store;
22170         this.ds.xmodule = this.xmodule || false;
22171     }
22172     
22173     this.toolbarItems = [];
22174     if (config.items) {
22175         this.toolbarItems = config.items;
22176     }
22177     
22178     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22179     
22180     this.cursor = 0;
22181     
22182     if (this.ds) { 
22183         this.bind(this.ds);
22184     }
22185     
22186     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22187     
22188 };
22189
22190 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22191     /**
22192      * @cfg {Roo.data.Store} dataSource
22193      * The underlying data store providing the paged data
22194      */
22195     /**
22196      * @cfg {String/HTMLElement/Element} container
22197      * container The id or element that will contain the toolbar
22198      */
22199     /**
22200      * @cfg {Boolean} displayInfo
22201      * True to display the displayMsg (defaults to false)
22202      */
22203     /**
22204      * @cfg {Number} pageSize
22205      * The number of records to display per page (defaults to 20)
22206      */
22207     pageSize: 20,
22208     /**
22209      * @cfg {String} displayMsg
22210      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22211      */
22212     displayMsg : 'Displaying {0} - {1} of {2}',
22213     /**
22214      * @cfg {String} emptyMsg
22215      * The message to display when no records are found (defaults to "No data to display")
22216      */
22217     emptyMsg : 'No data to display',
22218     /**
22219      * Customizable piece of the default paging text (defaults to "Page")
22220      * @type String
22221      */
22222     beforePageText : "Page",
22223     /**
22224      * Customizable piece of the default paging text (defaults to "of %0")
22225      * @type String
22226      */
22227     afterPageText : "of {0}",
22228     /**
22229      * Customizable piece of the default paging text (defaults to "First Page")
22230      * @type String
22231      */
22232     firstText : "First Page",
22233     /**
22234      * Customizable piece of the default paging text (defaults to "Previous Page")
22235      * @type String
22236      */
22237     prevText : "Previous Page",
22238     /**
22239      * Customizable piece of the default paging text (defaults to "Next Page")
22240      * @type String
22241      */
22242     nextText : "Next Page",
22243     /**
22244      * Customizable piece of the default paging text (defaults to "Last Page")
22245      * @type String
22246      */
22247     lastText : "Last Page",
22248     /**
22249      * Customizable piece of the default paging text (defaults to "Refresh")
22250      * @type String
22251      */
22252     refreshText : "Refresh",
22253
22254     buttons : false,
22255     // private
22256     onRender : function(ct, position) 
22257     {
22258         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22259         this.navgroup.parentId = this.id;
22260         this.navgroup.onRender(this.el, null);
22261         // add the buttons to the navgroup
22262         
22263         if(this.displayInfo){
22264             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22265             this.displayEl = this.el.select('.x-paging-info', true).first();
22266 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22267 //            this.displayEl = navel.el.select('span',true).first();
22268         }
22269         
22270         var _this = this;
22271         
22272         if(this.buttons){
22273             Roo.each(_this.buttons, function(e){ // this might need to use render????
22274                Roo.factory(e).onRender(_this.el, null);
22275             });
22276         }
22277             
22278         Roo.each(_this.toolbarItems, function(e) {
22279             _this.navgroup.addItem(e);
22280         });
22281         
22282         
22283         this.first = this.navgroup.addItem({
22284             tooltip: this.firstText,
22285             cls: "prev",
22286             icon : 'fa fa-backward',
22287             disabled: true,
22288             preventDefault: true,
22289             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22290         });
22291         
22292         this.prev =  this.navgroup.addItem({
22293             tooltip: this.prevText,
22294             cls: "prev",
22295             icon : 'fa fa-step-backward',
22296             disabled: true,
22297             preventDefault: true,
22298             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22299         });
22300     //this.addSeparator();
22301         
22302         
22303         var field = this.navgroup.addItem( {
22304             tagtype : 'span',
22305             cls : 'x-paging-position',
22306             
22307             html : this.beforePageText  +
22308                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22309                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22310          } ); //?? escaped?
22311         
22312         this.field = field.el.select('input', true).first();
22313         this.field.on("keydown", this.onPagingKeydown, this);
22314         this.field.on("focus", function(){this.dom.select();});
22315     
22316     
22317         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22318         //this.field.setHeight(18);
22319         //this.addSeparator();
22320         this.next = this.navgroup.addItem({
22321             tooltip: this.nextText,
22322             cls: "next",
22323             html : ' <i class="fa fa-step-forward">',
22324             disabled: true,
22325             preventDefault: true,
22326             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22327         });
22328         this.last = this.navgroup.addItem({
22329             tooltip: this.lastText,
22330             icon : 'fa fa-forward',
22331             cls: "next",
22332             disabled: true,
22333             preventDefault: true,
22334             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22335         });
22336     //this.addSeparator();
22337         this.loading = this.navgroup.addItem({
22338             tooltip: this.refreshText,
22339             icon: 'fa fa-refresh',
22340             preventDefault: true,
22341             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22342         });
22343         
22344     },
22345
22346     // private
22347     updateInfo : function(){
22348         if(this.displayEl){
22349             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22350             var msg = count == 0 ?
22351                 this.emptyMsg :
22352                 String.format(
22353                     this.displayMsg,
22354                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22355                 );
22356             this.displayEl.update(msg);
22357         }
22358     },
22359
22360     // private
22361     onLoad : function(ds, r, o){
22362        this.cursor = o.params ? o.params.start : 0;
22363        var d = this.getPageData(),
22364             ap = d.activePage,
22365             ps = d.pages;
22366         
22367        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22368        this.field.dom.value = ap;
22369        this.first.setDisabled(ap == 1);
22370        this.prev.setDisabled(ap == 1);
22371        this.next.setDisabled(ap == ps);
22372        this.last.setDisabled(ap == ps);
22373        this.loading.enable();
22374        this.updateInfo();
22375     },
22376
22377     // private
22378     getPageData : function(){
22379         var total = this.ds.getTotalCount();
22380         return {
22381             total : total,
22382             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22383             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22384         };
22385     },
22386
22387     // private
22388     onLoadError : function(){
22389         this.loading.enable();
22390     },
22391
22392     // private
22393     onPagingKeydown : function(e){
22394         var k = e.getKey();
22395         var d = this.getPageData();
22396         if(k == e.RETURN){
22397             var v = this.field.dom.value, pageNum;
22398             if(!v || isNaN(pageNum = parseInt(v, 10))){
22399                 this.field.dom.value = d.activePage;
22400                 return;
22401             }
22402             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22403             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22404             e.stopEvent();
22405         }
22406         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))
22407         {
22408           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22409           this.field.dom.value = pageNum;
22410           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22411           e.stopEvent();
22412         }
22413         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22414         {
22415           var v = this.field.dom.value, pageNum; 
22416           var increment = (e.shiftKey) ? 10 : 1;
22417           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22418                 increment *= -1;
22419           }
22420           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22421             this.field.dom.value = d.activePage;
22422             return;
22423           }
22424           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22425           {
22426             this.field.dom.value = parseInt(v, 10) + increment;
22427             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22428             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22429           }
22430           e.stopEvent();
22431         }
22432     },
22433
22434     // private
22435     beforeLoad : function(){
22436         if(this.loading){
22437             this.loading.disable();
22438         }
22439     },
22440
22441     // private
22442     onClick : function(which){
22443         
22444         var ds = this.ds;
22445         if (!ds) {
22446             return;
22447         }
22448         
22449         switch(which){
22450             case "first":
22451                 ds.load({params:{start: 0, limit: this.pageSize}});
22452             break;
22453             case "prev":
22454                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22455             break;
22456             case "next":
22457                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22458             break;
22459             case "last":
22460                 var total = ds.getTotalCount();
22461                 var extra = total % this.pageSize;
22462                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22463                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22464             break;
22465             case "refresh":
22466                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22467             break;
22468         }
22469     },
22470
22471     /**
22472      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22473      * @param {Roo.data.Store} store The data store to unbind
22474      */
22475     unbind : function(ds){
22476         ds.un("beforeload", this.beforeLoad, this);
22477         ds.un("load", this.onLoad, this);
22478         ds.un("loadexception", this.onLoadError, this);
22479         ds.un("remove", this.updateInfo, this);
22480         ds.un("add", this.updateInfo, this);
22481         this.ds = undefined;
22482     },
22483
22484     /**
22485      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22486      * @param {Roo.data.Store} store The data store to bind
22487      */
22488     bind : function(ds){
22489         ds.on("beforeload", this.beforeLoad, this);
22490         ds.on("load", this.onLoad, this);
22491         ds.on("loadexception", this.onLoadError, this);
22492         ds.on("remove", this.updateInfo, this);
22493         ds.on("add", this.updateInfo, this);
22494         this.ds = ds;
22495     }
22496 });/*
22497  * - LGPL
22498  *
22499  * element
22500  * 
22501  */
22502
22503 /**
22504  * @class Roo.bootstrap.MessageBar
22505  * @extends Roo.bootstrap.Component
22506  * Bootstrap MessageBar class
22507  * @cfg {String} html contents of the MessageBar
22508  * @cfg {String} weight (info | success | warning | danger) default info
22509  * @cfg {String} beforeClass insert the bar before the given class
22510  * @cfg {Boolean} closable (true | false) default false
22511  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22512  * 
22513  * @constructor
22514  * Create a new Element
22515  * @param {Object} config The config object
22516  */
22517
22518 Roo.bootstrap.MessageBar = function(config){
22519     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22520 };
22521
22522 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22523     
22524     html: '',
22525     weight: 'info',
22526     closable: false,
22527     fixed: false,
22528     beforeClass: 'bootstrap-sticky-wrap',
22529     
22530     getAutoCreate : function(){
22531         
22532         var cfg = {
22533             tag: 'div',
22534             cls: 'alert alert-dismissable alert-' + this.weight,
22535             cn: [
22536                 {
22537                     tag: 'span',
22538                     cls: 'message',
22539                     html: this.html || ''
22540                 }
22541             ]
22542         };
22543         
22544         if(this.fixed){
22545             cfg.cls += ' alert-messages-fixed';
22546         }
22547         
22548         if(this.closable){
22549             cfg.cn.push({
22550                 tag: 'button',
22551                 cls: 'close',
22552                 html: 'x'
22553             });
22554         }
22555         
22556         return cfg;
22557     },
22558     
22559     onRender : function(ct, position)
22560     {
22561         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22562         
22563         if(!this.el){
22564             var cfg = Roo.apply({},  this.getAutoCreate());
22565             cfg.id = Roo.id();
22566             
22567             if (this.cls) {
22568                 cfg.cls += ' ' + this.cls;
22569             }
22570             if (this.style) {
22571                 cfg.style = this.style;
22572             }
22573             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22574             
22575             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22576         }
22577         
22578         this.el.select('>button.close').on('click', this.hide, this);
22579         
22580     },
22581     
22582     show : function()
22583     {
22584         if (!this.rendered) {
22585             this.render();
22586         }
22587         
22588         this.el.show();
22589         
22590         this.fireEvent('show', this);
22591         
22592     },
22593     
22594     hide : function()
22595     {
22596         if (!this.rendered) {
22597             this.render();
22598         }
22599         
22600         this.el.hide();
22601         
22602         this.fireEvent('hide', this);
22603     },
22604     
22605     update : function()
22606     {
22607 //        var e = this.el.dom.firstChild;
22608 //        
22609 //        if(this.closable){
22610 //            e = e.nextSibling;
22611 //        }
22612 //        
22613 //        e.data = this.html || '';
22614
22615         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22616     }
22617    
22618 });
22619
22620  
22621
22622      /*
22623  * - LGPL
22624  *
22625  * Graph
22626  * 
22627  */
22628
22629
22630 /**
22631  * @class Roo.bootstrap.Graph
22632  * @extends Roo.bootstrap.Component
22633  * Bootstrap Graph class
22634 > Prameters
22635  -sm {number} sm 4
22636  -md {number} md 5
22637  @cfg {String} graphtype  bar | vbar | pie
22638  @cfg {number} g_x coodinator | centre x (pie)
22639  @cfg {number} g_y coodinator | centre y (pie)
22640  @cfg {number} g_r radius (pie)
22641  @cfg {number} g_height height of the chart (respected by all elements in the set)
22642  @cfg {number} g_width width of the chart (respected by all elements in the set)
22643  @cfg {Object} title The title of the chart
22644     
22645  -{Array}  values
22646  -opts (object) options for the chart 
22647      o {
22648      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22649      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22650      o vgutter (number)
22651      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.
22652      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22653      o to
22654      o stretch (boolean)
22655      o }
22656  -opts (object) options for the pie
22657      o{
22658      o cut
22659      o startAngle (number)
22660      o endAngle (number)
22661      } 
22662  *
22663  * @constructor
22664  * Create a new Input
22665  * @param {Object} config The config object
22666  */
22667
22668 Roo.bootstrap.Graph = function(config){
22669     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22670     
22671     this.addEvents({
22672         // img events
22673         /**
22674          * @event click
22675          * The img click event for the img.
22676          * @param {Roo.EventObject} e
22677          */
22678         "click" : true
22679     });
22680 };
22681
22682 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22683     
22684     sm: 4,
22685     md: 5,
22686     graphtype: 'bar',
22687     g_height: 250,
22688     g_width: 400,
22689     g_x: 50,
22690     g_y: 50,
22691     g_r: 30,
22692     opts:{
22693         //g_colors: this.colors,
22694         g_type: 'soft',
22695         g_gutter: '20%'
22696
22697     },
22698     title : false,
22699
22700     getAutoCreate : function(){
22701         
22702         var cfg = {
22703             tag: 'div',
22704             html : null
22705         };
22706         
22707         
22708         return  cfg;
22709     },
22710
22711     onRender : function(ct,position){
22712         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22713         this.raphael = Raphael(this.el.dom);
22714         
22715                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22716                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22717                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22718                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22719                 /*
22720                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22721                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22722                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22723                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22724                 
22725                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22726                 r.barchart(330, 10, 300, 220, data1);
22727                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22728                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22729                 */
22730                 
22731                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22732                 // r.barchart(30, 30, 560, 250,  xdata, {
22733                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22734                 //     axis : "0 0 1 1",
22735                 //     axisxlabels :  xdata
22736                 //     //yvalues : cols,
22737                    
22738                 // });
22739 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22740 //        
22741 //        this.load(null,xdata,{
22742 //                axis : "0 0 1 1",
22743 //                axisxlabels :  xdata
22744 //                });
22745
22746     },
22747
22748     load : function(graphtype,xdata,opts){
22749         this.raphael.clear();
22750         if(!graphtype) {
22751             graphtype = this.graphtype;
22752         }
22753         if(!opts){
22754             opts = this.opts;
22755         }
22756         var r = this.raphael,
22757             fin = function () {
22758                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22759             },
22760             fout = function () {
22761                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22762             },
22763             pfin = function() {
22764                 this.sector.stop();
22765                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22766
22767                 if (this.label) {
22768                     this.label[0].stop();
22769                     this.label[0].attr({ r: 7.5 });
22770                     this.label[1].attr({ "font-weight": 800 });
22771                 }
22772             },
22773             pfout = function() {
22774                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22775
22776                 if (this.label) {
22777                     this.label[0].animate({ r: 5 }, 500, "bounce");
22778                     this.label[1].attr({ "font-weight": 400 });
22779                 }
22780             };
22781
22782         switch(graphtype){
22783             case 'bar':
22784                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22785                 break;
22786             case 'hbar':
22787                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22788                 break;
22789             case 'pie':
22790 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22791 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22792 //            
22793                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22794                 
22795                 break;
22796
22797         }
22798         
22799         if(this.title){
22800             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22801         }
22802         
22803     },
22804     
22805     setTitle: function(o)
22806     {
22807         this.title = o;
22808     },
22809     
22810     initEvents: function() {
22811         
22812         if(!this.href){
22813             this.el.on('click', this.onClick, this);
22814         }
22815     },
22816     
22817     onClick : function(e)
22818     {
22819         Roo.log('img onclick');
22820         this.fireEvent('click', this, e);
22821     }
22822    
22823 });
22824
22825  
22826 /*
22827  * - LGPL
22828  *
22829  * numberBox
22830  * 
22831  */
22832 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22833
22834 /**
22835  * @class Roo.bootstrap.dash.NumberBox
22836  * @extends Roo.bootstrap.Component
22837  * Bootstrap NumberBox class
22838  * @cfg {String} headline Box headline
22839  * @cfg {String} content Box content
22840  * @cfg {String} icon Box icon
22841  * @cfg {String} footer Footer text
22842  * @cfg {String} fhref Footer href
22843  * 
22844  * @constructor
22845  * Create a new NumberBox
22846  * @param {Object} config The config object
22847  */
22848
22849
22850 Roo.bootstrap.dash.NumberBox = function(config){
22851     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22852     
22853 };
22854
22855 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22856     
22857     headline : '',
22858     content : '',
22859     icon : '',
22860     footer : '',
22861     fhref : '',
22862     ficon : '',
22863     
22864     getAutoCreate : function(){
22865         
22866         var cfg = {
22867             tag : 'div',
22868             cls : 'small-box ',
22869             cn : [
22870                 {
22871                     tag : 'div',
22872                     cls : 'inner',
22873                     cn :[
22874                         {
22875                             tag : 'h3',
22876                             cls : 'roo-headline',
22877                             html : this.headline
22878                         },
22879                         {
22880                             tag : 'p',
22881                             cls : 'roo-content',
22882                             html : this.content
22883                         }
22884                     ]
22885                 }
22886             ]
22887         };
22888         
22889         if(this.icon){
22890             cfg.cn.push({
22891                 tag : 'div',
22892                 cls : 'icon',
22893                 cn :[
22894                     {
22895                         tag : 'i',
22896                         cls : 'ion ' + this.icon
22897                     }
22898                 ]
22899             });
22900         }
22901         
22902         if(this.footer){
22903             var footer = {
22904                 tag : 'a',
22905                 cls : 'small-box-footer',
22906                 href : this.fhref || '#',
22907                 html : this.footer
22908             };
22909             
22910             cfg.cn.push(footer);
22911             
22912         }
22913         
22914         return  cfg;
22915     },
22916
22917     onRender : function(ct,position){
22918         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22919
22920
22921        
22922                 
22923     },
22924
22925     setHeadline: function (value)
22926     {
22927         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22928     },
22929     
22930     setFooter: function (value, href)
22931     {
22932         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22933         
22934         if(href){
22935             this.el.select('a.small-box-footer',true).first().attr('href', href);
22936         }
22937         
22938     },
22939
22940     setContent: function (value)
22941     {
22942         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22943     },
22944
22945     initEvents: function() 
22946     {   
22947         
22948     }
22949     
22950 });
22951
22952  
22953 /*
22954  * - LGPL
22955  *
22956  * TabBox
22957  * 
22958  */
22959 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22960
22961 /**
22962  * @class Roo.bootstrap.dash.TabBox
22963  * @extends Roo.bootstrap.Component
22964  * Bootstrap TabBox class
22965  * @cfg {String} title Title of the TabBox
22966  * @cfg {String} icon Icon of the TabBox
22967  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22968  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22969  * 
22970  * @constructor
22971  * Create a new TabBox
22972  * @param {Object} config The config object
22973  */
22974
22975
22976 Roo.bootstrap.dash.TabBox = function(config){
22977     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22978     this.addEvents({
22979         // raw events
22980         /**
22981          * @event addpane
22982          * When a pane is added
22983          * @param {Roo.bootstrap.dash.TabPane} pane
22984          */
22985         "addpane" : true,
22986         /**
22987          * @event activatepane
22988          * When a pane is activated
22989          * @param {Roo.bootstrap.dash.TabPane} pane
22990          */
22991         "activatepane" : true
22992         
22993          
22994     });
22995     
22996     this.panes = [];
22997 };
22998
22999 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23000
23001     title : '',
23002     icon : false,
23003     showtabs : true,
23004     tabScrollable : false,
23005     
23006     getChildContainer : function()
23007     {
23008         return this.el.select('.tab-content', true).first();
23009     },
23010     
23011     getAutoCreate : function(){
23012         
23013         var header = {
23014             tag: 'li',
23015             cls: 'pull-left header',
23016             html: this.title,
23017             cn : []
23018         };
23019         
23020         if(this.icon){
23021             header.cn.push({
23022                 tag: 'i',
23023                 cls: 'fa ' + this.icon
23024             });
23025         }
23026         
23027         var h = {
23028             tag: 'ul',
23029             cls: 'nav nav-tabs pull-right',
23030             cn: [
23031                 header
23032             ]
23033         };
23034         
23035         if(this.tabScrollable){
23036             h = {
23037                 tag: 'div',
23038                 cls: 'tab-header',
23039                 cn: [
23040                     {
23041                         tag: 'ul',
23042                         cls: 'nav nav-tabs pull-right',
23043                         cn: [
23044                             header
23045                         ]
23046                     }
23047                 ]
23048             };
23049         }
23050         
23051         var cfg = {
23052             tag: 'div',
23053             cls: 'nav-tabs-custom',
23054             cn: [
23055                 h,
23056                 {
23057                     tag: 'div',
23058                     cls: 'tab-content no-padding',
23059                     cn: []
23060                 }
23061             ]
23062         };
23063
23064         return  cfg;
23065     },
23066     initEvents : function()
23067     {
23068         //Roo.log('add add pane handler');
23069         this.on('addpane', this.onAddPane, this);
23070     },
23071      /**
23072      * Updates the box title
23073      * @param {String} html to set the title to.
23074      */
23075     setTitle : function(value)
23076     {
23077         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23078     },
23079     onAddPane : function(pane)
23080     {
23081         this.panes.push(pane);
23082         //Roo.log('addpane');
23083         //Roo.log(pane);
23084         // tabs are rendere left to right..
23085         if(!this.showtabs){
23086             return;
23087         }
23088         
23089         var ctr = this.el.select('.nav-tabs', true).first();
23090          
23091          
23092         var existing = ctr.select('.nav-tab',true);
23093         var qty = existing.getCount();;
23094         
23095         
23096         var tab = ctr.createChild({
23097             tag : 'li',
23098             cls : 'nav-tab' + (qty ? '' : ' active'),
23099             cn : [
23100                 {
23101                     tag : 'a',
23102                     href:'#',
23103                     html : pane.title
23104                 }
23105             ]
23106         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23107         pane.tab = tab;
23108         
23109         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23110         if (!qty) {
23111             pane.el.addClass('active');
23112         }
23113         
23114                 
23115     },
23116     onTabClick : function(ev,un,ob,pane)
23117     {
23118         //Roo.log('tab - prev default');
23119         ev.preventDefault();
23120         
23121         
23122         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23123         pane.tab.addClass('active');
23124         //Roo.log(pane.title);
23125         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23126         // technically we should have a deactivate event.. but maybe add later.
23127         // and it should not de-activate the selected tab...
23128         this.fireEvent('activatepane', pane);
23129         pane.el.addClass('active');
23130         pane.fireEvent('activate');
23131         
23132         
23133     },
23134     
23135     getActivePane : function()
23136     {
23137         var r = false;
23138         Roo.each(this.panes, function(p) {
23139             if(p.el.hasClass('active')){
23140                 r = p;
23141                 return false;
23142             }
23143             
23144             return;
23145         });
23146         
23147         return r;
23148     }
23149     
23150     
23151 });
23152
23153  
23154 /*
23155  * - LGPL
23156  *
23157  * Tab pane
23158  * 
23159  */
23160 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23161 /**
23162  * @class Roo.bootstrap.TabPane
23163  * @extends Roo.bootstrap.Component
23164  * Bootstrap TabPane class
23165  * @cfg {Boolean} active (false | true) Default false
23166  * @cfg {String} title title of panel
23167
23168  * 
23169  * @constructor
23170  * Create a new TabPane
23171  * @param {Object} config The config object
23172  */
23173
23174 Roo.bootstrap.dash.TabPane = function(config){
23175     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23176     
23177     this.addEvents({
23178         // raw events
23179         /**
23180          * @event activate
23181          * When a pane is activated
23182          * @param {Roo.bootstrap.dash.TabPane} pane
23183          */
23184         "activate" : true
23185          
23186     });
23187 };
23188
23189 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23190     
23191     active : false,
23192     title : '',
23193     
23194     // the tabBox that this is attached to.
23195     tab : false,
23196      
23197     getAutoCreate : function() 
23198     {
23199         var cfg = {
23200             tag: 'div',
23201             cls: 'tab-pane'
23202         };
23203         
23204         if(this.active){
23205             cfg.cls += ' active';
23206         }
23207         
23208         return cfg;
23209     },
23210     initEvents  : function()
23211     {
23212         //Roo.log('trigger add pane handler');
23213         this.parent().fireEvent('addpane', this)
23214     },
23215     
23216      /**
23217      * Updates the tab title 
23218      * @param {String} html to set the title to.
23219      */
23220     setTitle: function(str)
23221     {
23222         if (!this.tab) {
23223             return;
23224         }
23225         this.title = str;
23226         this.tab.select('a', true).first().dom.innerHTML = str;
23227         
23228     }
23229     
23230     
23231     
23232 });
23233
23234  
23235
23236
23237  /*
23238  * - LGPL
23239  *
23240  * menu
23241  * 
23242  */
23243 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23244
23245 /**
23246  * @class Roo.bootstrap.menu.Menu
23247  * @extends Roo.bootstrap.Component
23248  * Bootstrap Menu class - container for Menu
23249  * @cfg {String} html Text of the menu
23250  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23251  * @cfg {String} icon Font awesome icon
23252  * @cfg {String} pos Menu align to (top | bottom) default bottom
23253  * 
23254  * 
23255  * @constructor
23256  * Create a new Menu
23257  * @param {Object} config The config object
23258  */
23259
23260
23261 Roo.bootstrap.menu.Menu = function(config){
23262     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23263     
23264     this.addEvents({
23265         /**
23266          * @event beforeshow
23267          * Fires before this menu is displayed
23268          * @param {Roo.bootstrap.menu.Menu} this
23269          */
23270         beforeshow : true,
23271         /**
23272          * @event beforehide
23273          * Fires before this menu is hidden
23274          * @param {Roo.bootstrap.menu.Menu} this
23275          */
23276         beforehide : true,
23277         /**
23278          * @event show
23279          * Fires after this menu is displayed
23280          * @param {Roo.bootstrap.menu.Menu} this
23281          */
23282         show : true,
23283         /**
23284          * @event hide
23285          * Fires after this menu is hidden
23286          * @param {Roo.bootstrap.menu.Menu} this
23287          */
23288         hide : true,
23289         /**
23290          * @event click
23291          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23292          * @param {Roo.bootstrap.menu.Menu} this
23293          * @param {Roo.EventObject} e
23294          */
23295         click : true
23296     });
23297     
23298 };
23299
23300 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23301     
23302     submenu : false,
23303     html : '',
23304     weight : 'default',
23305     icon : false,
23306     pos : 'bottom',
23307     
23308     
23309     getChildContainer : function() {
23310         if(this.isSubMenu){
23311             return this.el;
23312         }
23313         
23314         return this.el.select('ul.dropdown-menu', true).first();  
23315     },
23316     
23317     getAutoCreate : function()
23318     {
23319         var text = [
23320             {
23321                 tag : 'span',
23322                 cls : 'roo-menu-text',
23323                 html : this.html
23324             }
23325         ];
23326         
23327         if(this.icon){
23328             text.unshift({
23329                 tag : 'i',
23330                 cls : 'fa ' + this.icon
23331             })
23332         }
23333         
23334         
23335         var cfg = {
23336             tag : 'div',
23337             cls : 'btn-group',
23338             cn : [
23339                 {
23340                     tag : 'button',
23341                     cls : 'dropdown-button btn btn-' + this.weight,
23342                     cn : text
23343                 },
23344                 {
23345                     tag : 'button',
23346                     cls : 'dropdown-toggle btn btn-' + this.weight,
23347                     cn : [
23348                         {
23349                             tag : 'span',
23350                             cls : 'caret'
23351                         }
23352                     ]
23353                 },
23354                 {
23355                     tag : 'ul',
23356                     cls : 'dropdown-menu'
23357                 }
23358             ]
23359             
23360         };
23361         
23362         if(this.pos == 'top'){
23363             cfg.cls += ' dropup';
23364         }
23365         
23366         if(this.isSubMenu){
23367             cfg = {
23368                 tag : 'ul',
23369                 cls : 'dropdown-menu'
23370             }
23371         }
23372         
23373         return cfg;
23374     },
23375     
23376     onRender : function(ct, position)
23377     {
23378         this.isSubMenu = ct.hasClass('dropdown-submenu');
23379         
23380         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23381     },
23382     
23383     initEvents : function() 
23384     {
23385         if(this.isSubMenu){
23386             return;
23387         }
23388         
23389         this.hidden = true;
23390         
23391         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23392         this.triggerEl.on('click', this.onTriggerPress, this);
23393         
23394         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23395         this.buttonEl.on('click', this.onClick, this);
23396         
23397     },
23398     
23399     list : function()
23400     {
23401         if(this.isSubMenu){
23402             return this.el;
23403         }
23404         
23405         return this.el.select('ul.dropdown-menu', true).first();
23406     },
23407     
23408     onClick : function(e)
23409     {
23410         this.fireEvent("click", this, e);
23411     },
23412     
23413     onTriggerPress  : function(e)
23414     {   
23415         if (this.isVisible()) {
23416             this.hide();
23417         } else {
23418             this.show();
23419         }
23420     },
23421     
23422     isVisible : function(){
23423         return !this.hidden;
23424     },
23425     
23426     show : function()
23427     {
23428         this.fireEvent("beforeshow", this);
23429         
23430         this.hidden = false;
23431         this.el.addClass('open');
23432         
23433         Roo.get(document).on("mouseup", this.onMouseUp, this);
23434         
23435         this.fireEvent("show", this);
23436         
23437         
23438     },
23439     
23440     hide : function()
23441     {
23442         this.fireEvent("beforehide", this);
23443         
23444         this.hidden = true;
23445         this.el.removeClass('open');
23446         
23447         Roo.get(document).un("mouseup", this.onMouseUp);
23448         
23449         this.fireEvent("hide", this);
23450     },
23451     
23452     onMouseUp : function()
23453     {
23454         this.hide();
23455     }
23456     
23457 });
23458
23459  
23460  /*
23461  * - LGPL
23462  *
23463  * menu item
23464  * 
23465  */
23466 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23467
23468 /**
23469  * @class Roo.bootstrap.menu.Item
23470  * @extends Roo.bootstrap.Component
23471  * Bootstrap MenuItem class
23472  * @cfg {Boolean} submenu (true | false) default false
23473  * @cfg {String} html text of the item
23474  * @cfg {String} href the link
23475  * @cfg {Boolean} disable (true | false) default false
23476  * @cfg {Boolean} preventDefault (true | false) default true
23477  * @cfg {String} icon Font awesome icon
23478  * @cfg {String} pos Submenu align to (left | right) default right 
23479  * 
23480  * 
23481  * @constructor
23482  * Create a new Item
23483  * @param {Object} config The config object
23484  */
23485
23486
23487 Roo.bootstrap.menu.Item = function(config){
23488     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23489     this.addEvents({
23490         /**
23491          * @event mouseover
23492          * Fires when the mouse is hovering over this menu
23493          * @param {Roo.bootstrap.menu.Item} this
23494          * @param {Roo.EventObject} e
23495          */
23496         mouseover : true,
23497         /**
23498          * @event mouseout
23499          * Fires when the mouse exits this menu
23500          * @param {Roo.bootstrap.menu.Item} this
23501          * @param {Roo.EventObject} e
23502          */
23503         mouseout : true,
23504         // raw events
23505         /**
23506          * @event click
23507          * The raw click event for the entire grid.
23508          * @param {Roo.EventObject} e
23509          */
23510         click : true
23511     });
23512 };
23513
23514 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23515     
23516     submenu : false,
23517     href : '',
23518     html : '',
23519     preventDefault: true,
23520     disable : false,
23521     icon : false,
23522     pos : 'right',
23523     
23524     getAutoCreate : function()
23525     {
23526         var text = [
23527             {
23528                 tag : 'span',
23529                 cls : 'roo-menu-item-text',
23530                 html : this.html
23531             }
23532         ];
23533         
23534         if(this.icon){
23535             text.unshift({
23536                 tag : 'i',
23537                 cls : 'fa ' + this.icon
23538             })
23539         }
23540         
23541         var cfg = {
23542             tag : 'li',
23543             cn : [
23544                 {
23545                     tag : 'a',
23546                     href : this.href || '#',
23547                     cn : text
23548                 }
23549             ]
23550         };
23551         
23552         if(this.disable){
23553             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23554         }
23555         
23556         if(this.submenu){
23557             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23558             
23559             if(this.pos == 'left'){
23560                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23561             }
23562         }
23563         
23564         return cfg;
23565     },
23566     
23567     initEvents : function() 
23568     {
23569         this.el.on('mouseover', this.onMouseOver, this);
23570         this.el.on('mouseout', this.onMouseOut, this);
23571         
23572         this.el.select('a', true).first().on('click', this.onClick, this);
23573         
23574     },
23575     
23576     onClick : function(e)
23577     {
23578         if(this.preventDefault){
23579             e.preventDefault();
23580         }
23581         
23582         this.fireEvent("click", this, e);
23583     },
23584     
23585     onMouseOver : function(e)
23586     {
23587         if(this.submenu && this.pos == 'left'){
23588             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23589         }
23590         
23591         this.fireEvent("mouseover", this, e);
23592     },
23593     
23594     onMouseOut : function(e)
23595     {
23596         this.fireEvent("mouseout", this, e);
23597     }
23598 });
23599
23600  
23601
23602  /*
23603  * - LGPL
23604  *
23605  * menu separator
23606  * 
23607  */
23608 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23609
23610 /**
23611  * @class Roo.bootstrap.menu.Separator
23612  * @extends Roo.bootstrap.Component
23613  * Bootstrap Separator class
23614  * 
23615  * @constructor
23616  * Create a new Separator
23617  * @param {Object} config The config object
23618  */
23619
23620
23621 Roo.bootstrap.menu.Separator = function(config){
23622     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23623 };
23624
23625 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23626     
23627     getAutoCreate : function(){
23628         var cfg = {
23629             tag : 'li',
23630             cls: 'divider'
23631         };
23632         
23633         return cfg;
23634     }
23635    
23636 });
23637
23638  
23639
23640  /*
23641  * - LGPL
23642  *
23643  * Tooltip
23644  * 
23645  */
23646
23647 /**
23648  * @class Roo.bootstrap.Tooltip
23649  * Bootstrap Tooltip class
23650  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23651  * to determine which dom element triggers the tooltip.
23652  * 
23653  * It needs to add support for additional attributes like tooltip-position
23654  * 
23655  * @constructor
23656  * Create a new Toolti
23657  * @param {Object} config The config object
23658  */
23659
23660 Roo.bootstrap.Tooltip = function(config){
23661     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23662 };
23663
23664 Roo.apply(Roo.bootstrap.Tooltip, {
23665     /**
23666      * @function init initialize tooltip monitoring.
23667      * @static
23668      */
23669     currentEl : false,
23670     currentTip : false,
23671     currentRegion : false,
23672     
23673     //  init : delay?
23674     
23675     init : function()
23676     {
23677         Roo.get(document).on('mouseover', this.enter ,this);
23678         Roo.get(document).on('mouseout', this.leave, this);
23679          
23680         
23681         this.currentTip = new Roo.bootstrap.Tooltip();
23682     },
23683     
23684     enter : function(ev)
23685     {
23686         var dom = ev.getTarget();
23687         
23688         //Roo.log(['enter',dom]);
23689         var el = Roo.fly(dom);
23690         if (this.currentEl) {
23691             //Roo.log(dom);
23692             //Roo.log(this.currentEl);
23693             //Roo.log(this.currentEl.contains(dom));
23694             if (this.currentEl == el) {
23695                 return;
23696             }
23697             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23698                 return;
23699             }
23700
23701         }
23702         
23703         if (this.currentTip.el) {
23704             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23705         }    
23706         //Roo.log(ev);
23707         var bindEl = el;
23708         
23709         // you can not look for children, as if el is the body.. then everythign is the child..
23710         if (!el.attr('tooltip')) { //
23711             if (!el.select("[tooltip]").elements.length) {
23712                 return;
23713             }
23714             // is the mouse over this child...?
23715             bindEl = el.select("[tooltip]").first();
23716             var xy = ev.getXY();
23717             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23718                 //Roo.log("not in region.");
23719                 return;
23720             }
23721             //Roo.log("child element over..");
23722             
23723         }
23724         this.currentEl = bindEl;
23725         this.currentTip.bind(bindEl);
23726         this.currentRegion = Roo.lib.Region.getRegion(dom);
23727         this.currentTip.enter();
23728         
23729     },
23730     leave : function(ev)
23731     {
23732         var dom = ev.getTarget();
23733         //Roo.log(['leave',dom]);
23734         if (!this.currentEl) {
23735             return;
23736         }
23737         
23738         
23739         if (dom != this.currentEl.dom) {
23740             return;
23741         }
23742         var xy = ev.getXY();
23743         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23744             return;
23745         }
23746         // only activate leave if mouse cursor is outside... bounding box..
23747         
23748         
23749         
23750         
23751         if (this.currentTip) {
23752             this.currentTip.leave();
23753         }
23754         //Roo.log('clear currentEl');
23755         this.currentEl = false;
23756         
23757         
23758     },
23759     alignment : {
23760         'left' : ['r-l', [-2,0], 'right'],
23761         'right' : ['l-r', [2,0], 'left'],
23762         'bottom' : ['t-b', [0,2], 'top'],
23763         'top' : [ 'b-t', [0,-2], 'bottom']
23764     }
23765     
23766 });
23767
23768
23769 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23770     
23771     
23772     bindEl : false,
23773     
23774     delay : null, // can be { show : 300 , hide: 500}
23775     
23776     timeout : null,
23777     
23778     hoverState : null, //???
23779     
23780     placement : 'bottom', 
23781     
23782     getAutoCreate : function(){
23783     
23784         var cfg = {
23785            cls : 'tooltip',
23786            role : 'tooltip',
23787            cn : [
23788                 {
23789                     cls : 'tooltip-arrow'
23790                 },
23791                 {
23792                     cls : 'tooltip-inner'
23793                 }
23794            ]
23795         };
23796         
23797         return cfg;
23798     },
23799     bind : function(el)
23800     {
23801         this.bindEl = el;
23802     },
23803       
23804     
23805     enter : function () {
23806        
23807         if (this.timeout != null) {
23808             clearTimeout(this.timeout);
23809         }
23810         
23811         this.hoverState = 'in';
23812          //Roo.log("enter - show");
23813         if (!this.delay || !this.delay.show) {
23814             this.show();
23815             return;
23816         }
23817         var _t = this;
23818         this.timeout = setTimeout(function () {
23819             if (_t.hoverState == 'in') {
23820                 _t.show();
23821             }
23822         }, this.delay.show);
23823     },
23824     leave : function()
23825     {
23826         clearTimeout(this.timeout);
23827     
23828         this.hoverState = 'out';
23829          if (!this.delay || !this.delay.hide) {
23830             this.hide();
23831             return;
23832         }
23833        
23834         var _t = this;
23835         this.timeout = setTimeout(function () {
23836             //Roo.log("leave - timeout");
23837             
23838             if (_t.hoverState == 'out') {
23839                 _t.hide();
23840                 Roo.bootstrap.Tooltip.currentEl = false;
23841             }
23842         }, delay);
23843     },
23844     
23845     show : function ()
23846     {
23847         if (!this.el) {
23848             this.render(document.body);
23849         }
23850         // set content.
23851         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23852         
23853         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23854         
23855         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23856         
23857         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23858         
23859         var placement = typeof this.placement == 'function' ?
23860             this.placement.call(this, this.el, on_el) :
23861             this.placement;
23862             
23863         var autoToken = /\s?auto?\s?/i;
23864         var autoPlace = autoToken.test(placement);
23865         if (autoPlace) {
23866             placement = placement.replace(autoToken, '') || 'top';
23867         }
23868         
23869         //this.el.detach()
23870         //this.el.setXY([0,0]);
23871         this.el.show();
23872         //this.el.dom.style.display='block';
23873         
23874         //this.el.appendTo(on_el);
23875         
23876         var p = this.getPosition();
23877         var box = this.el.getBox();
23878         
23879         if (autoPlace) {
23880             // fixme..
23881         }
23882         
23883         var align = Roo.bootstrap.Tooltip.alignment[placement];
23884         
23885         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23886         
23887         if(placement == 'top' || placement == 'bottom'){
23888             if(xy[0] < 0){
23889                 placement = 'right';
23890             }
23891             
23892             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23893                 placement = 'left';
23894             }
23895         }
23896         
23897         align = Roo.bootstrap.Tooltip.alignment[placement];
23898         
23899         this.el.alignTo(this.bindEl, align[0],align[1]);
23900         //var arrow = this.el.select('.arrow',true).first();
23901         //arrow.set(align[2], 
23902         
23903         this.el.addClass(placement);
23904         
23905         this.el.addClass('in fade');
23906         
23907         this.hoverState = null;
23908         
23909         if (this.el.hasClass('fade')) {
23910             // fade it?
23911         }
23912         
23913     },
23914     hide : function()
23915     {
23916          
23917         if (!this.el) {
23918             return;
23919         }
23920         //this.el.setXY([0,0]);
23921         this.el.removeClass('in');
23922         //this.el.hide();
23923         
23924     }
23925     
23926 });
23927  
23928
23929  /*
23930  * - LGPL
23931  *
23932  * Location Picker
23933  * 
23934  */
23935
23936 /**
23937  * @class Roo.bootstrap.LocationPicker
23938  * @extends Roo.bootstrap.Component
23939  * Bootstrap LocationPicker class
23940  * @cfg {Number} latitude Position when init default 0
23941  * @cfg {Number} longitude Position when init default 0
23942  * @cfg {Number} zoom default 15
23943  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23944  * @cfg {Boolean} mapTypeControl default false
23945  * @cfg {Boolean} disableDoubleClickZoom default false
23946  * @cfg {Boolean} scrollwheel default true
23947  * @cfg {Boolean} streetViewControl default false
23948  * @cfg {Number} radius default 0
23949  * @cfg {String} locationName
23950  * @cfg {Boolean} draggable default true
23951  * @cfg {Boolean} enableAutocomplete default false
23952  * @cfg {Boolean} enableReverseGeocode default true
23953  * @cfg {String} markerTitle
23954  * 
23955  * @constructor
23956  * Create a new LocationPicker
23957  * @param {Object} config The config object
23958  */
23959
23960
23961 Roo.bootstrap.LocationPicker = function(config){
23962     
23963     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23964     
23965     this.addEvents({
23966         /**
23967          * @event initial
23968          * Fires when the picker initialized.
23969          * @param {Roo.bootstrap.LocationPicker} this
23970          * @param {Google Location} location
23971          */
23972         initial : true,
23973         /**
23974          * @event positionchanged
23975          * Fires when the picker position changed.
23976          * @param {Roo.bootstrap.LocationPicker} this
23977          * @param {Google Location} location
23978          */
23979         positionchanged : true,
23980         /**
23981          * @event resize
23982          * Fires when the map resize.
23983          * @param {Roo.bootstrap.LocationPicker} this
23984          */
23985         resize : true,
23986         /**
23987          * @event show
23988          * Fires when the map show.
23989          * @param {Roo.bootstrap.LocationPicker} this
23990          */
23991         show : true,
23992         /**
23993          * @event hide
23994          * Fires when the map hide.
23995          * @param {Roo.bootstrap.LocationPicker} this
23996          */
23997         hide : true,
23998         /**
23999          * @event mapClick
24000          * Fires when click the map.
24001          * @param {Roo.bootstrap.LocationPicker} this
24002          * @param {Map event} e
24003          */
24004         mapClick : true,
24005         /**
24006          * @event mapRightClick
24007          * Fires when right click the map.
24008          * @param {Roo.bootstrap.LocationPicker} this
24009          * @param {Map event} e
24010          */
24011         mapRightClick : true,
24012         /**
24013          * @event markerClick
24014          * Fires when click the marker.
24015          * @param {Roo.bootstrap.LocationPicker} this
24016          * @param {Map event} e
24017          */
24018         markerClick : true,
24019         /**
24020          * @event markerRightClick
24021          * Fires when right click the marker.
24022          * @param {Roo.bootstrap.LocationPicker} this
24023          * @param {Map event} e
24024          */
24025         markerRightClick : true,
24026         /**
24027          * @event OverlayViewDraw
24028          * Fires when OverlayView Draw
24029          * @param {Roo.bootstrap.LocationPicker} this
24030          */
24031         OverlayViewDraw : true,
24032         /**
24033          * @event OverlayViewOnAdd
24034          * Fires when OverlayView Draw
24035          * @param {Roo.bootstrap.LocationPicker} this
24036          */
24037         OverlayViewOnAdd : true,
24038         /**
24039          * @event OverlayViewOnRemove
24040          * Fires when OverlayView Draw
24041          * @param {Roo.bootstrap.LocationPicker} this
24042          */
24043         OverlayViewOnRemove : true,
24044         /**
24045          * @event OverlayViewShow
24046          * Fires when OverlayView Draw
24047          * @param {Roo.bootstrap.LocationPicker} this
24048          * @param {Pixel} cpx
24049          */
24050         OverlayViewShow : true,
24051         /**
24052          * @event OverlayViewHide
24053          * Fires when OverlayView Draw
24054          * @param {Roo.bootstrap.LocationPicker} this
24055          */
24056         OverlayViewHide : true,
24057         /**
24058          * @event loadexception
24059          * Fires when load google lib failed.
24060          * @param {Roo.bootstrap.LocationPicker} this
24061          */
24062         loadexception : true
24063     });
24064         
24065 };
24066
24067 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24068     
24069     gMapContext: false,
24070     
24071     latitude: 0,
24072     longitude: 0,
24073     zoom: 15,
24074     mapTypeId: false,
24075     mapTypeControl: false,
24076     disableDoubleClickZoom: false,
24077     scrollwheel: true,
24078     streetViewControl: false,
24079     radius: 0,
24080     locationName: '',
24081     draggable: true,
24082     enableAutocomplete: false,
24083     enableReverseGeocode: true,
24084     markerTitle: '',
24085     
24086     getAutoCreate: function()
24087     {
24088
24089         var cfg = {
24090             tag: 'div',
24091             cls: 'roo-location-picker'
24092         };
24093         
24094         return cfg
24095     },
24096     
24097     initEvents: function(ct, position)
24098     {       
24099         if(!this.el.getWidth() || this.isApplied()){
24100             return;
24101         }
24102         
24103         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24104         
24105         this.initial();
24106     },
24107     
24108     initial: function()
24109     {
24110         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24111             this.fireEvent('loadexception', this);
24112             return;
24113         }
24114         
24115         if(!this.mapTypeId){
24116             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24117         }
24118         
24119         this.gMapContext = this.GMapContext();
24120         
24121         this.initOverlayView();
24122         
24123         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24124         
24125         var _this = this;
24126                 
24127         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24128             _this.setPosition(_this.gMapContext.marker.position);
24129         });
24130         
24131         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24132             _this.fireEvent('mapClick', this, event);
24133             
24134         });
24135
24136         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24137             _this.fireEvent('mapRightClick', this, event);
24138             
24139         });
24140         
24141         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24142             _this.fireEvent('markerClick', this, event);
24143             
24144         });
24145
24146         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24147             _this.fireEvent('markerRightClick', this, event);
24148             
24149         });
24150         
24151         this.setPosition(this.gMapContext.location);
24152         
24153         this.fireEvent('initial', this, this.gMapContext.location);
24154     },
24155     
24156     initOverlayView: function()
24157     {
24158         var _this = this;
24159         
24160         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24161             
24162             draw: function()
24163             {
24164                 _this.fireEvent('OverlayViewDraw', _this);
24165             },
24166             
24167             onAdd: function()
24168             {
24169                 _this.fireEvent('OverlayViewOnAdd', _this);
24170             },
24171             
24172             onRemove: function()
24173             {
24174                 _this.fireEvent('OverlayViewOnRemove', _this);
24175             },
24176             
24177             show: function(cpx)
24178             {
24179                 _this.fireEvent('OverlayViewShow', _this, cpx);
24180             },
24181             
24182             hide: function()
24183             {
24184                 _this.fireEvent('OverlayViewHide', _this);
24185             }
24186             
24187         });
24188     },
24189     
24190     fromLatLngToContainerPixel: function(event)
24191     {
24192         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24193     },
24194     
24195     isApplied: function() 
24196     {
24197         return this.getGmapContext() == false ? false : true;
24198     },
24199     
24200     getGmapContext: function() 
24201     {
24202         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24203     },
24204     
24205     GMapContext: function() 
24206     {
24207         var position = new google.maps.LatLng(this.latitude, this.longitude);
24208         
24209         var _map = new google.maps.Map(this.el.dom, {
24210             center: position,
24211             zoom: this.zoom,
24212             mapTypeId: this.mapTypeId,
24213             mapTypeControl: this.mapTypeControl,
24214             disableDoubleClickZoom: this.disableDoubleClickZoom,
24215             scrollwheel: this.scrollwheel,
24216             streetViewControl: this.streetViewControl,
24217             locationName: this.locationName,
24218             draggable: this.draggable,
24219             enableAutocomplete: this.enableAutocomplete,
24220             enableReverseGeocode: this.enableReverseGeocode
24221         });
24222         
24223         var _marker = new google.maps.Marker({
24224             position: position,
24225             map: _map,
24226             title: this.markerTitle,
24227             draggable: this.draggable
24228         });
24229         
24230         return {
24231             map: _map,
24232             marker: _marker,
24233             circle: null,
24234             location: position,
24235             radius: this.radius,
24236             locationName: this.locationName,
24237             addressComponents: {
24238                 formatted_address: null,
24239                 addressLine1: null,
24240                 addressLine2: null,
24241                 streetName: null,
24242                 streetNumber: null,
24243                 city: null,
24244                 district: null,
24245                 state: null,
24246                 stateOrProvince: null
24247             },
24248             settings: this,
24249             domContainer: this.el.dom,
24250             geodecoder: new google.maps.Geocoder()
24251         };
24252     },
24253     
24254     drawCircle: function(center, radius, options) 
24255     {
24256         if (this.gMapContext.circle != null) {
24257             this.gMapContext.circle.setMap(null);
24258         }
24259         if (radius > 0) {
24260             radius *= 1;
24261             options = Roo.apply({}, options, {
24262                 strokeColor: "#0000FF",
24263                 strokeOpacity: .35,
24264                 strokeWeight: 2,
24265                 fillColor: "#0000FF",
24266                 fillOpacity: .2
24267             });
24268             
24269             options.map = this.gMapContext.map;
24270             options.radius = radius;
24271             options.center = center;
24272             this.gMapContext.circle = new google.maps.Circle(options);
24273             return this.gMapContext.circle;
24274         }
24275         
24276         return null;
24277     },
24278     
24279     setPosition: function(location) 
24280     {
24281         this.gMapContext.location = location;
24282         this.gMapContext.marker.setPosition(location);
24283         this.gMapContext.map.panTo(location);
24284         this.drawCircle(location, this.gMapContext.radius, {});
24285         
24286         var _this = this;
24287         
24288         if (this.gMapContext.settings.enableReverseGeocode) {
24289             this.gMapContext.geodecoder.geocode({
24290                 latLng: this.gMapContext.location
24291             }, function(results, status) {
24292                 
24293                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24294                     _this.gMapContext.locationName = results[0].formatted_address;
24295                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24296                     
24297                     _this.fireEvent('positionchanged', this, location);
24298                 }
24299             });
24300             
24301             return;
24302         }
24303         
24304         this.fireEvent('positionchanged', this, location);
24305     },
24306     
24307     resize: function()
24308     {
24309         google.maps.event.trigger(this.gMapContext.map, "resize");
24310         
24311         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24312         
24313         this.fireEvent('resize', this);
24314     },
24315     
24316     setPositionByLatLng: function(latitude, longitude)
24317     {
24318         this.setPosition(new google.maps.LatLng(latitude, longitude));
24319     },
24320     
24321     getCurrentPosition: function() 
24322     {
24323         return {
24324             latitude: this.gMapContext.location.lat(),
24325             longitude: this.gMapContext.location.lng()
24326         };
24327     },
24328     
24329     getAddressName: function() 
24330     {
24331         return this.gMapContext.locationName;
24332     },
24333     
24334     getAddressComponents: function() 
24335     {
24336         return this.gMapContext.addressComponents;
24337     },
24338     
24339     address_component_from_google_geocode: function(address_components) 
24340     {
24341         var result = {};
24342         
24343         for (var i = 0; i < address_components.length; i++) {
24344             var component = address_components[i];
24345             if (component.types.indexOf("postal_code") >= 0) {
24346                 result.postalCode = component.short_name;
24347             } else if (component.types.indexOf("street_number") >= 0) {
24348                 result.streetNumber = component.short_name;
24349             } else if (component.types.indexOf("route") >= 0) {
24350                 result.streetName = component.short_name;
24351             } else if (component.types.indexOf("neighborhood") >= 0) {
24352                 result.city = component.short_name;
24353             } else if (component.types.indexOf("locality") >= 0) {
24354                 result.city = component.short_name;
24355             } else if (component.types.indexOf("sublocality") >= 0) {
24356                 result.district = component.short_name;
24357             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24358                 result.stateOrProvince = component.short_name;
24359             } else if (component.types.indexOf("country") >= 0) {
24360                 result.country = component.short_name;
24361             }
24362         }
24363         
24364         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24365         result.addressLine2 = "";
24366         return result;
24367     },
24368     
24369     setZoomLevel: function(zoom)
24370     {
24371         this.gMapContext.map.setZoom(zoom);
24372     },
24373     
24374     show: function()
24375     {
24376         if(!this.el){
24377             return;
24378         }
24379         
24380         this.el.show();
24381         
24382         this.resize();
24383         
24384         this.fireEvent('show', this);
24385     },
24386     
24387     hide: function()
24388     {
24389         if(!this.el){
24390             return;
24391         }
24392         
24393         this.el.hide();
24394         
24395         this.fireEvent('hide', this);
24396     }
24397     
24398 });
24399
24400 Roo.apply(Roo.bootstrap.LocationPicker, {
24401     
24402     OverlayView : function(map, options)
24403     {
24404         options = options || {};
24405         
24406         this.setMap(map);
24407     }
24408     
24409     
24410 });/*
24411  * - LGPL
24412  *
24413  * Alert
24414  * 
24415  */
24416
24417 /**
24418  * @class Roo.bootstrap.Alert
24419  * @extends Roo.bootstrap.Component
24420  * Bootstrap Alert class
24421  * @cfg {String} title The title of alert
24422  * @cfg {String} html The content of alert
24423  * @cfg {String} weight (  success | info | warning | danger )
24424  * @cfg {String} faicon font-awesomeicon
24425  * 
24426  * @constructor
24427  * Create a new alert
24428  * @param {Object} config The config object
24429  */
24430
24431
24432 Roo.bootstrap.Alert = function(config){
24433     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24434     
24435 };
24436
24437 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24438     
24439     title: '',
24440     html: '',
24441     weight: false,
24442     faicon: false,
24443     
24444     getAutoCreate : function()
24445     {
24446         
24447         var cfg = {
24448             tag : 'div',
24449             cls : 'alert',
24450             cn : [
24451                 {
24452                     tag : 'i',
24453                     cls : 'roo-alert-icon'
24454                     
24455                 },
24456                 {
24457                     tag : 'b',
24458                     cls : 'roo-alert-title',
24459                     html : this.title
24460                 },
24461                 {
24462                     tag : 'span',
24463                     cls : 'roo-alert-text',
24464                     html : this.html
24465                 }
24466             ]
24467         };
24468         
24469         if(this.faicon){
24470             cfg.cn[0].cls += ' fa ' + this.faicon;
24471         }
24472         
24473         if(this.weight){
24474             cfg.cls += ' alert-' + this.weight;
24475         }
24476         
24477         return cfg;
24478     },
24479     
24480     initEvents: function() 
24481     {
24482         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24483     },
24484     
24485     setTitle : function(str)
24486     {
24487         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24488     },
24489     
24490     setText : function(str)
24491     {
24492         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24493     },
24494     
24495     setWeight : function(weight)
24496     {
24497         if(this.weight){
24498             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24499         }
24500         
24501         this.weight = weight;
24502         
24503         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24504     },
24505     
24506     setIcon : function(icon)
24507     {
24508         if(this.faicon){
24509             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24510         }
24511         
24512         this.faicon = icon;
24513         
24514         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24515     },
24516     
24517     hide: function() 
24518     {
24519         this.el.hide();   
24520     },
24521     
24522     show: function() 
24523     {  
24524         this.el.show();   
24525     }
24526     
24527 });
24528
24529  
24530 /*
24531 * Licence: LGPL
24532 */
24533
24534 /**
24535  * @class Roo.bootstrap.UploadCropbox
24536  * @extends Roo.bootstrap.Component
24537  * Bootstrap UploadCropbox class
24538  * @cfg {String} emptyText show when image has been loaded
24539  * @cfg {String} rotateNotify show when image too small to rotate
24540  * @cfg {Number} errorTimeout default 3000
24541  * @cfg {Number} minWidth default 300
24542  * @cfg {Number} minHeight default 300
24543  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24544  * @cfg {Boolean} isDocument (true|false) default false
24545  * @cfg {String} url action url
24546  * @cfg {String} paramName default 'imageUpload'
24547  * @cfg {String} method default POST
24548  * @cfg {Boolean} loadMask (true|false) default true
24549  * @cfg {Boolean} loadingText default 'Loading...'
24550  * 
24551  * @constructor
24552  * Create a new UploadCropbox
24553  * @param {Object} config The config object
24554  */
24555
24556 Roo.bootstrap.UploadCropbox = function(config){
24557     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24558     
24559     this.addEvents({
24560         /**
24561          * @event beforeselectfile
24562          * Fire before select file
24563          * @param {Roo.bootstrap.UploadCropbox} this
24564          */
24565         "beforeselectfile" : true,
24566         /**
24567          * @event initial
24568          * Fire after initEvent
24569          * @param {Roo.bootstrap.UploadCropbox} this
24570          */
24571         "initial" : true,
24572         /**
24573          * @event crop
24574          * Fire after initEvent
24575          * @param {Roo.bootstrap.UploadCropbox} this
24576          * @param {String} data
24577          */
24578         "crop" : true,
24579         /**
24580          * @event prepare
24581          * Fire when preparing the file data
24582          * @param {Roo.bootstrap.UploadCropbox} this
24583          * @param {Object} file
24584          */
24585         "prepare" : true,
24586         /**
24587          * @event exception
24588          * Fire when get exception
24589          * @param {Roo.bootstrap.UploadCropbox} this
24590          * @param {XMLHttpRequest} xhr
24591          */
24592         "exception" : true,
24593         /**
24594          * @event beforeloadcanvas
24595          * Fire before load the canvas
24596          * @param {Roo.bootstrap.UploadCropbox} this
24597          * @param {String} src
24598          */
24599         "beforeloadcanvas" : true,
24600         /**
24601          * @event trash
24602          * Fire when trash image
24603          * @param {Roo.bootstrap.UploadCropbox} this
24604          */
24605         "trash" : true,
24606         /**
24607          * @event download
24608          * Fire when download the image
24609          * @param {Roo.bootstrap.UploadCropbox} this
24610          */
24611         "download" : true,
24612         /**
24613          * @event footerbuttonclick
24614          * Fire when footerbuttonclick
24615          * @param {Roo.bootstrap.UploadCropbox} this
24616          * @param {String} type
24617          */
24618         "footerbuttonclick" : true,
24619         /**
24620          * @event resize
24621          * Fire when resize
24622          * @param {Roo.bootstrap.UploadCropbox} this
24623          */
24624         "resize" : true,
24625         /**
24626          * @event rotate
24627          * Fire when rotate the image
24628          * @param {Roo.bootstrap.UploadCropbox} this
24629          * @param {String} pos
24630          */
24631         "rotate" : true,
24632         /**
24633          * @event inspect
24634          * Fire when inspect the file
24635          * @param {Roo.bootstrap.UploadCropbox} this
24636          * @param {Object} file
24637          */
24638         "inspect" : true,
24639         /**
24640          * @event upload
24641          * Fire when xhr upload the file
24642          * @param {Roo.bootstrap.UploadCropbox} this
24643          * @param {Object} data
24644          */
24645         "upload" : true,
24646         /**
24647          * @event arrange
24648          * Fire when arrange the file data
24649          * @param {Roo.bootstrap.UploadCropbox} this
24650          * @param {Object} formData
24651          */
24652         "arrange" : true
24653     });
24654     
24655     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24656 };
24657
24658 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24659     
24660     emptyText : 'Click to upload image',
24661     rotateNotify : 'Image is too small to rotate',
24662     errorTimeout : 3000,
24663     scale : 0,
24664     baseScale : 1,
24665     rotate : 0,
24666     dragable : false,
24667     pinching : false,
24668     mouseX : 0,
24669     mouseY : 0,
24670     cropData : false,
24671     minWidth : 300,
24672     minHeight : 300,
24673     file : false,
24674     exif : {},
24675     baseRotate : 1,
24676     cropType : 'image/jpeg',
24677     buttons : false,
24678     canvasLoaded : false,
24679     isDocument : false,
24680     method : 'POST',
24681     paramName : 'imageUpload',
24682     loadMask : true,
24683     loadingText : 'Loading...',
24684     maskEl : false,
24685     
24686     getAutoCreate : function()
24687     {
24688         var cfg = {
24689             tag : 'div',
24690             cls : 'roo-upload-cropbox',
24691             cn : [
24692                 {
24693                     tag : 'input',
24694                     cls : 'roo-upload-cropbox-selector',
24695                     type : 'file'
24696                 },
24697                 {
24698                     tag : 'div',
24699                     cls : 'roo-upload-cropbox-body',
24700                     style : 'cursor:pointer',
24701                     cn : [
24702                         {
24703                             tag : 'div',
24704                             cls : 'roo-upload-cropbox-preview'
24705                         },
24706                         {
24707                             tag : 'div',
24708                             cls : 'roo-upload-cropbox-thumb'
24709                         },
24710                         {
24711                             tag : 'div',
24712                             cls : 'roo-upload-cropbox-empty-notify',
24713                             html : this.emptyText
24714                         },
24715                         {
24716                             tag : 'div',
24717                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24718                             html : this.rotateNotify
24719                         }
24720                     ]
24721                 },
24722                 {
24723                     tag : 'div',
24724                     cls : 'roo-upload-cropbox-footer',
24725                     cn : {
24726                         tag : 'div',
24727                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24728                         cn : []
24729                     }
24730                 }
24731             ]
24732         };
24733         
24734         return cfg;
24735     },
24736     
24737     onRender : function(ct, position)
24738     {
24739         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24740         
24741         if (this.buttons.length) {
24742             
24743             Roo.each(this.buttons, function(bb) {
24744                 
24745                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24746                 
24747                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24748                 
24749             }, this);
24750         }
24751         
24752         if(this.loadMask){
24753             this.maskEl = this.el;
24754         }
24755     },
24756     
24757     initEvents : function()
24758     {
24759         this.urlAPI = (window.createObjectURL && window) || 
24760                                 (window.URL && URL.revokeObjectURL && URL) || 
24761                                 (window.webkitURL && webkitURL);
24762                         
24763         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24764         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24765         
24766         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24767         this.selectorEl.hide();
24768         
24769         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24770         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24771         
24772         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24773         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24774         this.thumbEl.hide();
24775         
24776         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24777         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24778         
24779         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24780         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24781         this.errorEl.hide();
24782         
24783         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24784         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24785         this.footerEl.hide();
24786         
24787         this.setThumbBoxSize();
24788         
24789         this.bind();
24790         
24791         this.resize();
24792         
24793         this.fireEvent('initial', this);
24794     },
24795
24796     bind : function()
24797     {
24798         var _this = this;
24799         
24800         window.addEventListener("resize", function() { _this.resize(); } );
24801         
24802         this.bodyEl.on('click', this.beforeSelectFile, this);
24803         
24804         if(Roo.isTouch){
24805             this.bodyEl.on('touchstart', this.onTouchStart, this);
24806             this.bodyEl.on('touchmove', this.onTouchMove, this);
24807             this.bodyEl.on('touchend', this.onTouchEnd, this);
24808         }
24809         
24810         if(!Roo.isTouch){
24811             this.bodyEl.on('mousedown', this.onMouseDown, this);
24812             this.bodyEl.on('mousemove', this.onMouseMove, this);
24813             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24814             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24815             Roo.get(document).on('mouseup', this.onMouseUp, this);
24816         }
24817         
24818         this.selectorEl.on('change', this.onFileSelected, this);
24819     },
24820     
24821     reset : function()
24822     {    
24823         this.scale = 0;
24824         this.baseScale = 1;
24825         this.rotate = 0;
24826         this.baseRotate = 1;
24827         this.dragable = false;
24828         this.pinching = false;
24829         this.mouseX = 0;
24830         this.mouseY = 0;
24831         this.cropData = false;
24832         this.notifyEl.dom.innerHTML = this.emptyText;
24833         
24834         this.selectorEl.dom.value = '';
24835         
24836     },
24837     
24838     resize : function()
24839     {
24840         if(this.fireEvent('resize', this) != false){
24841             this.setThumbBoxPosition();
24842             this.setCanvasPosition();
24843         }
24844     },
24845     
24846     onFooterButtonClick : function(e, el, o, type)
24847     {
24848         switch (type) {
24849             case 'rotate-left' :
24850                 this.onRotateLeft(e);
24851                 break;
24852             case 'rotate-right' :
24853                 this.onRotateRight(e);
24854                 break;
24855             case 'picture' :
24856                 this.beforeSelectFile(e);
24857                 break;
24858             case 'trash' :
24859                 this.trash(e);
24860                 break;
24861             case 'crop' :
24862                 this.crop(e);
24863                 break;
24864             case 'download' :
24865                 this.download(e);
24866                 break;
24867             default :
24868                 break;
24869         }
24870         
24871         this.fireEvent('footerbuttonclick', this, type);
24872     },
24873     
24874     beforeSelectFile : function(e)
24875     {
24876         e.preventDefault();
24877         
24878         if(this.fireEvent('beforeselectfile', this) != false){
24879             this.selectorEl.dom.click();
24880         }
24881     },
24882     
24883     onFileSelected : function(e)
24884     {
24885         e.preventDefault();
24886         
24887         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24888             return;
24889         }
24890         
24891         var file = this.selectorEl.dom.files[0];
24892         
24893         if(this.fireEvent('inspect', this, file) != false){
24894             this.prepare(file);
24895         }
24896         
24897     },
24898     
24899     trash : function(e)
24900     {
24901         this.fireEvent('trash', this);
24902     },
24903     
24904     download : function(e)
24905     {
24906         this.fireEvent('download', this);
24907     },
24908     
24909     loadCanvas : function(src)
24910     {   
24911         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24912             
24913             this.reset();
24914             
24915             this.imageEl = document.createElement('img');
24916             
24917             var _this = this;
24918             
24919             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24920             
24921             this.imageEl.src = src;
24922         }
24923     },
24924     
24925     onLoadCanvas : function()
24926     {   
24927         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24928         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24929         
24930         this.bodyEl.un('click', this.beforeSelectFile, this);
24931         
24932         this.notifyEl.hide();
24933         this.thumbEl.show();
24934         this.footerEl.show();
24935         
24936         this.baseRotateLevel();
24937         
24938         if(this.isDocument){
24939             this.setThumbBoxSize();
24940         }
24941         
24942         this.setThumbBoxPosition();
24943         
24944         this.baseScaleLevel();
24945         
24946         this.draw();
24947         
24948         this.resize();
24949         
24950         this.canvasLoaded = true;
24951         
24952         if(this.loadMask){
24953             this.maskEl.unmask();
24954         }
24955         
24956     },
24957     
24958     setCanvasPosition : function()
24959     {   
24960         if(!this.canvasEl){
24961             return;
24962         }
24963         
24964         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24965         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24966         
24967         this.previewEl.setLeft(pw);
24968         this.previewEl.setTop(ph);
24969         
24970     },
24971     
24972     onMouseDown : function(e)
24973     {   
24974         e.stopEvent();
24975         
24976         this.dragable = true;
24977         this.pinching = false;
24978         
24979         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24980             this.dragable = false;
24981             return;
24982         }
24983         
24984         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24985         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24986         
24987     },
24988     
24989     onMouseMove : function(e)
24990     {   
24991         e.stopEvent();
24992         
24993         if(!this.canvasLoaded){
24994             return;
24995         }
24996         
24997         if (!this.dragable){
24998             return;
24999         }
25000         
25001         var minX = Math.ceil(this.thumbEl.getLeft(true));
25002         var minY = Math.ceil(this.thumbEl.getTop(true));
25003         
25004         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25005         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25006         
25007         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25008         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25009         
25010         x = x - this.mouseX;
25011         y = y - this.mouseY;
25012         
25013         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25014         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25015         
25016         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25017         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25018         
25019         this.previewEl.setLeft(bgX);
25020         this.previewEl.setTop(bgY);
25021         
25022         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25023         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25024     },
25025     
25026     onMouseUp : function(e)
25027     {   
25028         e.stopEvent();
25029         
25030         this.dragable = false;
25031     },
25032     
25033     onMouseWheel : function(e)
25034     {   
25035         e.stopEvent();
25036         
25037         this.startScale = this.scale;
25038         
25039         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25040         
25041         if(!this.zoomable()){
25042             this.scale = this.startScale;
25043             return;
25044         }
25045         
25046         this.draw();
25047         
25048         return;
25049     },
25050     
25051     zoomable : function()
25052     {
25053         var minScale = this.thumbEl.getWidth() / this.minWidth;
25054         
25055         if(this.minWidth < this.minHeight){
25056             minScale = this.thumbEl.getHeight() / this.minHeight;
25057         }
25058         
25059         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25060         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25061         
25062         if(
25063                 this.isDocument &&
25064                 (this.rotate == 0 || this.rotate == 180) && 
25065                 (
25066                     width > this.imageEl.OriginWidth || 
25067                     height > this.imageEl.OriginHeight ||
25068                     (width < this.minWidth && height < this.minHeight)
25069                 )
25070         ){
25071             return false;
25072         }
25073         
25074         if(
25075                 this.isDocument &&
25076                 (this.rotate == 90 || this.rotate == 270) && 
25077                 (
25078                     width > this.imageEl.OriginWidth || 
25079                     height > this.imageEl.OriginHeight ||
25080                     (width < this.minHeight && height < this.minWidth)
25081                 )
25082         ){
25083             return false;
25084         }
25085         
25086         if(
25087                 !this.isDocument &&
25088                 (this.rotate == 0 || this.rotate == 180) && 
25089                 (
25090                     width < this.minWidth || 
25091                     width > this.imageEl.OriginWidth || 
25092                     height < this.minHeight || 
25093                     height > this.imageEl.OriginHeight
25094                 )
25095         ){
25096             return false;
25097         }
25098         
25099         if(
25100                 !this.isDocument &&
25101                 (this.rotate == 90 || this.rotate == 270) && 
25102                 (
25103                     width < this.minHeight || 
25104                     width > this.imageEl.OriginWidth || 
25105                     height < this.minWidth || 
25106                     height > this.imageEl.OriginHeight
25107                 )
25108         ){
25109             return false;
25110         }
25111         
25112         return true;
25113         
25114     },
25115     
25116     onRotateLeft : function(e)
25117     {   
25118         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25119             
25120             var minScale = this.thumbEl.getWidth() / this.minWidth;
25121             
25122             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25123             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25124             
25125             this.startScale = this.scale;
25126             
25127             while (this.getScaleLevel() < minScale){
25128             
25129                 this.scale = this.scale + 1;
25130                 
25131                 if(!this.zoomable()){
25132                     break;
25133                 }
25134                 
25135                 if(
25136                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25137                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25138                 ){
25139                     continue;
25140                 }
25141                 
25142                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25143
25144                 this.draw();
25145                 
25146                 return;
25147             }
25148             
25149             this.scale = this.startScale;
25150             
25151             this.onRotateFail();
25152             
25153             return false;
25154         }
25155         
25156         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25157
25158         if(this.isDocument){
25159             this.setThumbBoxSize();
25160             this.setThumbBoxPosition();
25161             this.setCanvasPosition();
25162         }
25163         
25164         this.draw();
25165         
25166         this.fireEvent('rotate', this, 'left');
25167         
25168     },
25169     
25170     onRotateRight : function(e)
25171     {
25172         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25173             
25174             var minScale = this.thumbEl.getWidth() / this.minWidth;
25175         
25176             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25177             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25178             
25179             this.startScale = this.scale;
25180             
25181             while (this.getScaleLevel() < minScale){
25182             
25183                 this.scale = this.scale + 1;
25184                 
25185                 if(!this.zoomable()){
25186                     break;
25187                 }
25188                 
25189                 if(
25190                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25191                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25192                 ){
25193                     continue;
25194                 }
25195                 
25196                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25197
25198                 this.draw();
25199                 
25200                 return;
25201             }
25202             
25203             this.scale = this.startScale;
25204             
25205             this.onRotateFail();
25206             
25207             return false;
25208         }
25209         
25210         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25211
25212         if(this.isDocument){
25213             this.setThumbBoxSize();
25214             this.setThumbBoxPosition();
25215             this.setCanvasPosition();
25216         }
25217         
25218         this.draw();
25219         
25220         this.fireEvent('rotate', this, 'right');
25221     },
25222     
25223     onRotateFail : function()
25224     {
25225         this.errorEl.show(true);
25226         
25227         var _this = this;
25228         
25229         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25230     },
25231     
25232     draw : function()
25233     {
25234         this.previewEl.dom.innerHTML = '';
25235         
25236         var canvasEl = document.createElement("canvas");
25237         
25238         var contextEl = canvasEl.getContext("2d");
25239         
25240         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25241         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25242         var center = this.imageEl.OriginWidth / 2;
25243         
25244         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25245             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25246             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25247             center = this.imageEl.OriginHeight / 2;
25248         }
25249         
25250         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25251         
25252         contextEl.translate(center, center);
25253         contextEl.rotate(this.rotate * Math.PI / 180);
25254
25255         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25256         
25257         this.canvasEl = document.createElement("canvas");
25258         
25259         this.contextEl = this.canvasEl.getContext("2d");
25260         
25261         switch (this.rotate) {
25262             case 0 :
25263                 
25264                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25265                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25266                 
25267                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25268                 
25269                 break;
25270             case 90 : 
25271                 
25272                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25273                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25274                 
25275                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25276                     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);
25277                     break;
25278                 }
25279                 
25280                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25281                 
25282                 break;
25283             case 180 :
25284                 
25285                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25286                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25287                 
25288                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25289                     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);
25290                     break;
25291                 }
25292                 
25293                 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);
25294                 
25295                 break;
25296             case 270 :
25297                 
25298                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25299                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25300         
25301                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25302                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25303                     break;
25304                 }
25305                 
25306                 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);
25307                 
25308                 break;
25309             default : 
25310                 break;
25311         }
25312         
25313         this.previewEl.appendChild(this.canvasEl);
25314         
25315         this.setCanvasPosition();
25316     },
25317     
25318     crop : function()
25319     {
25320         if(!this.canvasLoaded){
25321             return;
25322         }
25323         
25324         var imageCanvas = document.createElement("canvas");
25325         
25326         var imageContext = imageCanvas.getContext("2d");
25327         
25328         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25329         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25330         
25331         var center = imageCanvas.width / 2;
25332         
25333         imageContext.translate(center, center);
25334         
25335         imageContext.rotate(this.rotate * Math.PI / 180);
25336         
25337         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25338         
25339         var canvas = document.createElement("canvas");
25340         
25341         var context = canvas.getContext("2d");
25342                 
25343         canvas.width = this.minWidth;
25344         canvas.height = this.minHeight;
25345
25346         switch (this.rotate) {
25347             case 0 :
25348                 
25349                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25350                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25351                 
25352                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25353                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25354                 
25355                 var targetWidth = this.minWidth - 2 * x;
25356                 var targetHeight = this.minHeight - 2 * y;
25357                 
25358                 var scale = 1;
25359                 
25360                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25361                     scale = targetWidth / width;
25362                 }
25363                 
25364                 if(x > 0 && y == 0){
25365                     scale = targetHeight / height;
25366                 }
25367                 
25368                 if(x > 0 && y > 0){
25369                     scale = targetWidth / width;
25370                     
25371                     if(width < height){
25372                         scale = targetHeight / height;
25373                     }
25374                 }
25375                 
25376                 context.scale(scale, scale);
25377                 
25378                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25379                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25380
25381                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25382                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25383
25384                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25385                 
25386                 break;
25387             case 90 : 
25388                 
25389                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25390                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25391                 
25392                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25393                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25394                 
25395                 var targetWidth = this.minWidth - 2 * x;
25396                 var targetHeight = this.minHeight - 2 * y;
25397                 
25398                 var scale = 1;
25399                 
25400                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25401                     scale = targetWidth / width;
25402                 }
25403                 
25404                 if(x > 0 && y == 0){
25405                     scale = targetHeight / height;
25406                 }
25407                 
25408                 if(x > 0 && y > 0){
25409                     scale = targetWidth / width;
25410                     
25411                     if(width < height){
25412                         scale = targetHeight / height;
25413                     }
25414                 }
25415                 
25416                 context.scale(scale, scale);
25417                 
25418                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25419                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25420
25421                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25422                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25423                 
25424                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25425                 
25426                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25427                 
25428                 break;
25429             case 180 :
25430                 
25431                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25432                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25433                 
25434                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25435                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25436                 
25437                 var targetWidth = this.minWidth - 2 * x;
25438                 var targetHeight = this.minHeight - 2 * y;
25439                 
25440                 var scale = 1;
25441                 
25442                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25443                     scale = targetWidth / width;
25444                 }
25445                 
25446                 if(x > 0 && y == 0){
25447                     scale = targetHeight / height;
25448                 }
25449                 
25450                 if(x > 0 && y > 0){
25451                     scale = targetWidth / width;
25452                     
25453                     if(width < height){
25454                         scale = targetHeight / height;
25455                     }
25456                 }
25457                 
25458                 context.scale(scale, scale);
25459                 
25460                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25461                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25462
25463                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25464                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25465
25466                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25467                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25468                 
25469                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25470                 
25471                 break;
25472             case 270 :
25473                 
25474                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25475                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25476                 
25477                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25478                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25479                 
25480                 var targetWidth = this.minWidth - 2 * x;
25481                 var targetHeight = this.minHeight - 2 * y;
25482                 
25483                 var scale = 1;
25484                 
25485                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25486                     scale = targetWidth / width;
25487                 }
25488                 
25489                 if(x > 0 && y == 0){
25490                     scale = targetHeight / height;
25491                 }
25492                 
25493                 if(x > 0 && y > 0){
25494                     scale = targetWidth / width;
25495                     
25496                     if(width < height){
25497                         scale = targetHeight / height;
25498                     }
25499                 }
25500                 
25501                 context.scale(scale, scale);
25502                 
25503                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25504                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25505
25506                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25507                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25508                 
25509                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25510                 
25511                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25512                 
25513                 break;
25514             default : 
25515                 break;
25516         }
25517         
25518         this.cropData = canvas.toDataURL(this.cropType);
25519         
25520         if(this.fireEvent('crop', this, this.cropData) !== false){
25521             this.process(this.file, this.cropData);
25522         }
25523         
25524         return;
25525         
25526     },
25527     
25528     setThumbBoxSize : function()
25529     {
25530         var width, height;
25531         
25532         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25533             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25534             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25535             
25536             this.minWidth = width;
25537             this.minHeight = height;
25538             
25539             if(this.rotate == 90 || this.rotate == 270){
25540                 this.minWidth = height;
25541                 this.minHeight = width;
25542             }
25543         }
25544         
25545         height = 300;
25546         width = Math.ceil(this.minWidth * height / this.minHeight);
25547         
25548         if(this.minWidth > this.minHeight){
25549             width = 300;
25550             height = Math.ceil(this.minHeight * width / this.minWidth);
25551         }
25552         
25553         this.thumbEl.setStyle({
25554             width : width + 'px',
25555             height : height + 'px'
25556         });
25557
25558         return;
25559             
25560     },
25561     
25562     setThumbBoxPosition : function()
25563     {
25564         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25565         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25566         
25567         this.thumbEl.setLeft(x);
25568         this.thumbEl.setTop(y);
25569         
25570     },
25571     
25572     baseRotateLevel : function()
25573     {
25574         this.baseRotate = 1;
25575         
25576         if(
25577                 typeof(this.exif) != 'undefined' &&
25578                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25579                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25580         ){
25581             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25582         }
25583         
25584         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25585         
25586     },
25587     
25588     baseScaleLevel : function()
25589     {
25590         var width, height;
25591         
25592         if(this.isDocument){
25593             
25594             if(this.baseRotate == 6 || this.baseRotate == 8){
25595             
25596                 height = this.thumbEl.getHeight();
25597                 this.baseScale = height / this.imageEl.OriginWidth;
25598
25599                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25600                     width = this.thumbEl.getWidth();
25601                     this.baseScale = width / this.imageEl.OriginHeight;
25602                 }
25603
25604                 return;
25605             }
25606
25607             height = this.thumbEl.getHeight();
25608             this.baseScale = height / this.imageEl.OriginHeight;
25609
25610             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25611                 width = this.thumbEl.getWidth();
25612                 this.baseScale = width / this.imageEl.OriginWidth;
25613             }
25614
25615             return;
25616         }
25617         
25618         if(this.baseRotate == 6 || this.baseRotate == 8){
25619             
25620             width = this.thumbEl.getHeight();
25621             this.baseScale = width / this.imageEl.OriginHeight;
25622             
25623             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25624                 height = this.thumbEl.getWidth();
25625                 this.baseScale = height / this.imageEl.OriginHeight;
25626             }
25627             
25628             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25629                 height = this.thumbEl.getWidth();
25630                 this.baseScale = height / this.imageEl.OriginHeight;
25631                 
25632                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25633                     width = this.thumbEl.getHeight();
25634                     this.baseScale = width / this.imageEl.OriginWidth;
25635                 }
25636             }
25637             
25638             return;
25639         }
25640         
25641         width = this.thumbEl.getWidth();
25642         this.baseScale = width / this.imageEl.OriginWidth;
25643         
25644         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25645             height = this.thumbEl.getHeight();
25646             this.baseScale = height / this.imageEl.OriginHeight;
25647         }
25648         
25649         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25650             
25651             height = this.thumbEl.getHeight();
25652             this.baseScale = height / this.imageEl.OriginHeight;
25653             
25654             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25655                 width = this.thumbEl.getWidth();
25656                 this.baseScale = width / this.imageEl.OriginWidth;
25657             }
25658             
25659         }
25660         
25661         return;
25662     },
25663     
25664     getScaleLevel : function()
25665     {
25666         return this.baseScale * Math.pow(1.1, this.scale);
25667     },
25668     
25669     onTouchStart : function(e)
25670     {
25671         if(!this.canvasLoaded){
25672             this.beforeSelectFile(e);
25673             return;
25674         }
25675         
25676         var touches = e.browserEvent.touches;
25677         
25678         if(!touches){
25679             return;
25680         }
25681         
25682         if(touches.length == 1){
25683             this.onMouseDown(e);
25684             return;
25685         }
25686         
25687         if(touches.length != 2){
25688             return;
25689         }
25690         
25691         var coords = [];
25692         
25693         for(var i = 0, finger; finger = touches[i]; i++){
25694             coords.push(finger.pageX, finger.pageY);
25695         }
25696         
25697         var x = Math.pow(coords[0] - coords[2], 2);
25698         var y = Math.pow(coords[1] - coords[3], 2);
25699         
25700         this.startDistance = Math.sqrt(x + y);
25701         
25702         this.startScale = this.scale;
25703         
25704         this.pinching = true;
25705         this.dragable = false;
25706         
25707     },
25708     
25709     onTouchMove : function(e)
25710     {
25711         if(!this.pinching && !this.dragable){
25712             return;
25713         }
25714         
25715         var touches = e.browserEvent.touches;
25716         
25717         if(!touches){
25718             return;
25719         }
25720         
25721         if(this.dragable){
25722             this.onMouseMove(e);
25723             return;
25724         }
25725         
25726         var coords = [];
25727         
25728         for(var i = 0, finger; finger = touches[i]; i++){
25729             coords.push(finger.pageX, finger.pageY);
25730         }
25731         
25732         var x = Math.pow(coords[0] - coords[2], 2);
25733         var y = Math.pow(coords[1] - coords[3], 2);
25734         
25735         this.endDistance = Math.sqrt(x + y);
25736         
25737         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25738         
25739         if(!this.zoomable()){
25740             this.scale = this.startScale;
25741             return;
25742         }
25743         
25744         this.draw();
25745         
25746     },
25747     
25748     onTouchEnd : function(e)
25749     {
25750         this.pinching = false;
25751         this.dragable = false;
25752         
25753     },
25754     
25755     process : function(file, crop)
25756     {
25757         if(this.loadMask){
25758             this.maskEl.mask(this.loadingText);
25759         }
25760         
25761         this.xhr = new XMLHttpRequest();
25762         
25763         file.xhr = this.xhr;
25764
25765         this.xhr.open(this.method, this.url, true);
25766         
25767         var headers = {
25768             "Accept": "application/json",
25769             "Cache-Control": "no-cache",
25770             "X-Requested-With": "XMLHttpRequest"
25771         };
25772         
25773         for (var headerName in headers) {
25774             var headerValue = headers[headerName];
25775             if (headerValue) {
25776                 this.xhr.setRequestHeader(headerName, headerValue);
25777             }
25778         }
25779         
25780         var _this = this;
25781         
25782         this.xhr.onload = function()
25783         {
25784             _this.xhrOnLoad(_this.xhr);
25785         }
25786         
25787         this.xhr.onerror = function()
25788         {
25789             _this.xhrOnError(_this.xhr);
25790         }
25791         
25792         var formData = new FormData();
25793
25794         formData.append('returnHTML', 'NO');
25795         
25796         if(crop){
25797             formData.append('crop', crop);
25798         }
25799         
25800         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25801             formData.append(this.paramName, file, file.name);
25802         }
25803         
25804         if(typeof(file.filename) != 'undefined'){
25805             formData.append('filename', file.filename);
25806         }
25807         
25808         if(typeof(file.mimetype) != 'undefined'){
25809             formData.append('mimetype', file.mimetype);
25810         }
25811         
25812         if(this.fireEvent('arrange', this, formData) != false){
25813             this.xhr.send(formData);
25814         };
25815     },
25816     
25817     xhrOnLoad : function(xhr)
25818     {
25819         if(this.loadMask){
25820             this.maskEl.unmask();
25821         }
25822         
25823         if (xhr.readyState !== 4) {
25824             this.fireEvent('exception', this, xhr);
25825             return;
25826         }
25827
25828         var response = Roo.decode(xhr.responseText);
25829         
25830         if(!response.success){
25831             this.fireEvent('exception', this, xhr);
25832             return;
25833         }
25834         
25835         var response = Roo.decode(xhr.responseText);
25836         
25837         this.fireEvent('upload', this, response);
25838         
25839     },
25840     
25841     xhrOnError : function()
25842     {
25843         if(this.loadMask){
25844             this.maskEl.unmask();
25845         }
25846         
25847         Roo.log('xhr on error');
25848         
25849         var response = Roo.decode(xhr.responseText);
25850           
25851         Roo.log(response);
25852         
25853     },
25854     
25855     prepare : function(file)
25856     {   
25857         if(this.loadMask){
25858             this.maskEl.mask(this.loadingText);
25859         }
25860         
25861         this.file = false;
25862         this.exif = {};
25863         
25864         if(typeof(file) === 'string'){
25865             this.loadCanvas(file);
25866             return;
25867         }
25868         
25869         if(!file || !this.urlAPI){
25870             return;
25871         }
25872         
25873         this.file = file;
25874         this.cropType = file.type;
25875         
25876         var _this = this;
25877         
25878         if(this.fireEvent('prepare', this, this.file) != false){
25879             
25880             var reader = new FileReader();
25881             
25882             reader.onload = function (e) {
25883                 if (e.target.error) {
25884                     Roo.log(e.target.error);
25885                     return;
25886                 }
25887                 
25888                 var buffer = e.target.result,
25889                     dataView = new DataView(buffer),
25890                     offset = 2,
25891                     maxOffset = dataView.byteLength - 4,
25892                     markerBytes,
25893                     markerLength;
25894                 
25895                 if (dataView.getUint16(0) === 0xffd8) {
25896                     while (offset < maxOffset) {
25897                         markerBytes = dataView.getUint16(offset);
25898                         
25899                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25900                             markerLength = dataView.getUint16(offset + 2) + 2;
25901                             if (offset + markerLength > dataView.byteLength) {
25902                                 Roo.log('Invalid meta data: Invalid segment size.');
25903                                 break;
25904                             }
25905                             
25906                             if(markerBytes == 0xffe1){
25907                                 _this.parseExifData(
25908                                     dataView,
25909                                     offset,
25910                                     markerLength
25911                                 );
25912                             }
25913                             
25914                             offset += markerLength;
25915                             
25916                             continue;
25917                         }
25918                         
25919                         break;
25920                     }
25921                     
25922                 }
25923                 
25924                 var url = _this.urlAPI.createObjectURL(_this.file);
25925                 
25926                 _this.loadCanvas(url);
25927                 
25928                 return;
25929             }
25930             
25931             reader.readAsArrayBuffer(this.file);
25932             
25933         }
25934         
25935     },
25936     
25937     parseExifData : function(dataView, offset, length)
25938     {
25939         var tiffOffset = offset + 10,
25940             littleEndian,
25941             dirOffset;
25942     
25943         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25944             // No Exif data, might be XMP data instead
25945             return;
25946         }
25947         
25948         // Check for the ASCII code for "Exif" (0x45786966):
25949         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25950             // No Exif data, might be XMP data instead
25951             return;
25952         }
25953         if (tiffOffset + 8 > dataView.byteLength) {
25954             Roo.log('Invalid Exif data: Invalid segment size.');
25955             return;
25956         }
25957         // Check for the two null bytes:
25958         if (dataView.getUint16(offset + 8) !== 0x0000) {
25959             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25960             return;
25961         }
25962         // Check the byte alignment:
25963         switch (dataView.getUint16(tiffOffset)) {
25964         case 0x4949:
25965             littleEndian = true;
25966             break;
25967         case 0x4D4D:
25968             littleEndian = false;
25969             break;
25970         default:
25971             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25972             return;
25973         }
25974         // Check for the TIFF tag marker (0x002A):
25975         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25976             Roo.log('Invalid Exif data: Missing TIFF marker.');
25977             return;
25978         }
25979         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25980         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25981         
25982         this.parseExifTags(
25983             dataView,
25984             tiffOffset,
25985             tiffOffset + dirOffset,
25986             littleEndian
25987         );
25988     },
25989     
25990     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25991     {
25992         var tagsNumber,
25993             dirEndOffset,
25994             i;
25995         if (dirOffset + 6 > dataView.byteLength) {
25996             Roo.log('Invalid Exif data: Invalid directory offset.');
25997             return;
25998         }
25999         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26000         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26001         if (dirEndOffset + 4 > dataView.byteLength) {
26002             Roo.log('Invalid Exif data: Invalid directory size.');
26003             return;
26004         }
26005         for (i = 0; i < tagsNumber; i += 1) {
26006             this.parseExifTag(
26007                 dataView,
26008                 tiffOffset,
26009                 dirOffset + 2 + 12 * i, // tag offset
26010                 littleEndian
26011             );
26012         }
26013         // Return the offset to the next directory:
26014         return dataView.getUint32(dirEndOffset, littleEndian);
26015     },
26016     
26017     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26018     {
26019         var tag = dataView.getUint16(offset, littleEndian);
26020         
26021         this.exif[tag] = this.getExifValue(
26022             dataView,
26023             tiffOffset,
26024             offset,
26025             dataView.getUint16(offset + 2, littleEndian), // tag type
26026             dataView.getUint32(offset + 4, littleEndian), // tag length
26027             littleEndian
26028         );
26029     },
26030     
26031     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26032     {
26033         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26034             tagSize,
26035             dataOffset,
26036             values,
26037             i,
26038             str,
26039             c;
26040     
26041         if (!tagType) {
26042             Roo.log('Invalid Exif data: Invalid tag type.');
26043             return;
26044         }
26045         
26046         tagSize = tagType.size * length;
26047         // Determine if the value is contained in the dataOffset bytes,
26048         // or if the value at the dataOffset is a pointer to the actual data:
26049         dataOffset = tagSize > 4 ?
26050                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26051         if (dataOffset + tagSize > dataView.byteLength) {
26052             Roo.log('Invalid Exif data: Invalid data offset.');
26053             return;
26054         }
26055         if (length === 1) {
26056             return tagType.getValue(dataView, dataOffset, littleEndian);
26057         }
26058         values = [];
26059         for (i = 0; i < length; i += 1) {
26060             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26061         }
26062         
26063         if (tagType.ascii) {
26064             str = '';
26065             // Concatenate the chars:
26066             for (i = 0; i < values.length; i += 1) {
26067                 c = values[i];
26068                 // Ignore the terminating NULL byte(s):
26069                 if (c === '\u0000') {
26070                     break;
26071                 }
26072                 str += c;
26073             }
26074             return str;
26075         }
26076         return values;
26077     }
26078     
26079 });
26080
26081 Roo.apply(Roo.bootstrap.UploadCropbox, {
26082     tags : {
26083         'Orientation': 0x0112
26084     },
26085     
26086     Orientation: {
26087             1: 0, //'top-left',
26088 //            2: 'top-right',
26089             3: 180, //'bottom-right',
26090 //            4: 'bottom-left',
26091 //            5: 'left-top',
26092             6: 90, //'right-top',
26093 //            7: 'right-bottom',
26094             8: 270 //'left-bottom'
26095     },
26096     
26097     exifTagTypes : {
26098         // byte, 8-bit unsigned int:
26099         1: {
26100             getValue: function (dataView, dataOffset) {
26101                 return dataView.getUint8(dataOffset);
26102             },
26103             size: 1
26104         },
26105         // ascii, 8-bit byte:
26106         2: {
26107             getValue: function (dataView, dataOffset) {
26108                 return String.fromCharCode(dataView.getUint8(dataOffset));
26109             },
26110             size: 1,
26111             ascii: true
26112         },
26113         // short, 16 bit int:
26114         3: {
26115             getValue: function (dataView, dataOffset, littleEndian) {
26116                 return dataView.getUint16(dataOffset, littleEndian);
26117             },
26118             size: 2
26119         },
26120         // long, 32 bit int:
26121         4: {
26122             getValue: function (dataView, dataOffset, littleEndian) {
26123                 return dataView.getUint32(dataOffset, littleEndian);
26124             },
26125             size: 4
26126         },
26127         // rational = two long values, first is numerator, second is denominator:
26128         5: {
26129             getValue: function (dataView, dataOffset, littleEndian) {
26130                 return dataView.getUint32(dataOffset, littleEndian) /
26131                     dataView.getUint32(dataOffset + 4, littleEndian);
26132             },
26133             size: 8
26134         },
26135         // slong, 32 bit signed int:
26136         9: {
26137             getValue: function (dataView, dataOffset, littleEndian) {
26138                 return dataView.getInt32(dataOffset, littleEndian);
26139             },
26140             size: 4
26141         },
26142         // srational, two slongs, first is numerator, second is denominator:
26143         10: {
26144             getValue: function (dataView, dataOffset, littleEndian) {
26145                 return dataView.getInt32(dataOffset, littleEndian) /
26146                     dataView.getInt32(dataOffset + 4, littleEndian);
26147             },
26148             size: 8
26149         }
26150     },
26151     
26152     footer : {
26153         STANDARD : [
26154             {
26155                 tag : 'div',
26156                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26157                 action : 'rotate-left',
26158                 cn : [
26159                     {
26160                         tag : 'button',
26161                         cls : 'btn btn-default',
26162                         html : '<i class="fa fa-undo"></i>'
26163                     }
26164                 ]
26165             },
26166             {
26167                 tag : 'div',
26168                 cls : 'btn-group roo-upload-cropbox-picture',
26169                 action : 'picture',
26170                 cn : [
26171                     {
26172                         tag : 'button',
26173                         cls : 'btn btn-default',
26174                         html : '<i class="fa fa-picture-o"></i>'
26175                     }
26176                 ]
26177             },
26178             {
26179                 tag : 'div',
26180                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26181                 action : 'rotate-right',
26182                 cn : [
26183                     {
26184                         tag : 'button',
26185                         cls : 'btn btn-default',
26186                         html : '<i class="fa fa-repeat"></i>'
26187                     }
26188                 ]
26189             }
26190         ],
26191         DOCUMENT : [
26192             {
26193                 tag : 'div',
26194                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26195                 action : 'rotate-left',
26196                 cn : [
26197                     {
26198                         tag : 'button',
26199                         cls : 'btn btn-default',
26200                         html : '<i class="fa fa-undo"></i>'
26201                     }
26202                 ]
26203             },
26204             {
26205                 tag : 'div',
26206                 cls : 'btn-group roo-upload-cropbox-download',
26207                 action : 'download',
26208                 cn : [
26209                     {
26210                         tag : 'button',
26211                         cls : 'btn btn-default',
26212                         html : '<i class="fa fa-download"></i>'
26213                     }
26214                 ]
26215             },
26216             {
26217                 tag : 'div',
26218                 cls : 'btn-group roo-upload-cropbox-crop',
26219                 action : 'crop',
26220                 cn : [
26221                     {
26222                         tag : 'button',
26223                         cls : 'btn btn-default',
26224                         html : '<i class="fa fa-crop"></i>'
26225                     }
26226                 ]
26227             },
26228             {
26229                 tag : 'div',
26230                 cls : 'btn-group roo-upload-cropbox-trash',
26231                 action : 'trash',
26232                 cn : [
26233                     {
26234                         tag : 'button',
26235                         cls : 'btn btn-default',
26236                         html : '<i class="fa fa-trash"></i>'
26237                     }
26238                 ]
26239             },
26240             {
26241                 tag : 'div',
26242                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26243                 action : 'rotate-right',
26244                 cn : [
26245                     {
26246                         tag : 'button',
26247                         cls : 'btn btn-default',
26248                         html : '<i class="fa fa-repeat"></i>'
26249                     }
26250                 ]
26251             }
26252         ],
26253         ROTATOR : [
26254             {
26255                 tag : 'div',
26256                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26257                 action : 'rotate-left',
26258                 cn : [
26259                     {
26260                         tag : 'button',
26261                         cls : 'btn btn-default',
26262                         html : '<i class="fa fa-undo"></i>'
26263                     }
26264                 ]
26265             },
26266             {
26267                 tag : 'div',
26268                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26269                 action : 'rotate-right',
26270                 cn : [
26271                     {
26272                         tag : 'button',
26273                         cls : 'btn btn-default',
26274                         html : '<i class="fa fa-repeat"></i>'
26275                     }
26276                 ]
26277             }
26278         ]
26279     }
26280 });
26281
26282 /*
26283 * Licence: LGPL
26284 */
26285
26286 /**
26287  * @class Roo.bootstrap.DocumentManager
26288  * @extends Roo.bootstrap.Component
26289  * Bootstrap DocumentManager class
26290  * @cfg {String} paramName default 'imageUpload'
26291  * @cfg {String} method default POST
26292  * @cfg {String} url action url
26293  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26294  * @cfg {Boolean} multiple multiple upload default true
26295  * @cfg {Number} thumbSize default 300
26296  * @cfg {String} fieldLabel
26297  * @cfg {Number} labelWidth default 4
26298  * @cfg {String} labelAlign (left|top) default left
26299  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26300  * 
26301  * @constructor
26302  * Create a new DocumentManager
26303  * @param {Object} config The config object
26304  */
26305
26306 Roo.bootstrap.DocumentManager = function(config){
26307     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26308     
26309     this.addEvents({
26310         /**
26311          * @event initial
26312          * Fire when initial the DocumentManager
26313          * @param {Roo.bootstrap.DocumentManager} this
26314          */
26315         "initial" : true,
26316         /**
26317          * @event inspect
26318          * inspect selected file
26319          * @param {Roo.bootstrap.DocumentManager} this
26320          * @param {File} file
26321          */
26322         "inspect" : true,
26323         /**
26324          * @event exception
26325          * Fire when xhr load exception
26326          * @param {Roo.bootstrap.DocumentManager} this
26327          * @param {XMLHttpRequest} xhr
26328          */
26329         "exception" : true,
26330         /**
26331          * @event prepare
26332          * prepare the form data
26333          * @param {Roo.bootstrap.DocumentManager} this
26334          * @param {Object} formData
26335          */
26336         "prepare" : true,
26337         /**
26338          * @event remove
26339          * Fire when remove the file
26340          * @param {Roo.bootstrap.DocumentManager} this
26341          * @param {Object} file
26342          */
26343         "remove" : true,
26344         /**
26345          * @event refresh
26346          * Fire after refresh the file
26347          * @param {Roo.bootstrap.DocumentManager} this
26348          */
26349         "refresh" : true,
26350         /**
26351          * @event click
26352          * Fire after click the image
26353          * @param {Roo.bootstrap.DocumentManager} this
26354          * @param {Object} file
26355          */
26356         "click" : true,
26357         /**
26358          * @event edit
26359          * Fire when upload a image and editable set to true
26360          * @param {Roo.bootstrap.DocumentManager} this
26361          * @param {Object} file
26362          */
26363         "edit" : true,
26364         /**
26365          * @event beforeselectfile
26366          * Fire before select file
26367          * @param {Roo.bootstrap.DocumentManager} this
26368          */
26369         "beforeselectfile" : true,
26370         /**
26371          * @event process
26372          * Fire before process file
26373          * @param {Roo.bootstrap.DocumentManager} this
26374          * @param {Object} file
26375          */
26376         "process" : true
26377         
26378     });
26379 };
26380
26381 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26382     
26383     boxes : 0,
26384     inputName : '',
26385     thumbSize : 300,
26386     multiple : true,
26387     files : [],
26388     method : 'POST',
26389     url : '',
26390     paramName : 'imageUpload',
26391     fieldLabel : '',
26392     labelWidth : 4,
26393     labelAlign : 'left',
26394     editable : true,
26395     delegates : [],
26396     
26397     
26398     xhr : false, 
26399     
26400     getAutoCreate : function()
26401     {   
26402         var managerWidget = {
26403             tag : 'div',
26404             cls : 'roo-document-manager',
26405             cn : [
26406                 {
26407                     tag : 'input',
26408                     cls : 'roo-document-manager-selector',
26409                     type : 'file'
26410                 },
26411                 {
26412                     tag : 'div',
26413                     cls : 'roo-document-manager-uploader',
26414                     cn : [
26415                         {
26416                             tag : 'div',
26417                             cls : 'roo-document-manager-upload-btn',
26418                             html : '<i class="fa fa-plus"></i>'
26419                         }
26420                     ]
26421                     
26422                 }
26423             ]
26424         };
26425         
26426         var content = [
26427             {
26428                 tag : 'div',
26429                 cls : 'column col-md-12',
26430                 cn : managerWidget
26431             }
26432         ];
26433         
26434         if(this.fieldLabel.length){
26435             
26436             content = [
26437                 {
26438                     tag : 'div',
26439                     cls : 'column col-md-12',
26440                     html : this.fieldLabel
26441                 },
26442                 {
26443                     tag : 'div',
26444                     cls : 'column col-md-12',
26445                     cn : managerWidget
26446                 }
26447             ];
26448
26449             if(this.labelAlign == 'left'){
26450                 content = [
26451                     {
26452                         tag : 'div',
26453                         cls : 'column col-md-' + this.labelWidth,
26454                         html : this.fieldLabel
26455                     },
26456                     {
26457                         tag : 'div',
26458                         cls : 'column col-md-' + (12 - this.labelWidth),
26459                         cn : managerWidget
26460                     }
26461                 ];
26462                 
26463             }
26464         }
26465         
26466         var cfg = {
26467             tag : 'div',
26468             cls : 'row clearfix',
26469             cn : content
26470         };
26471         
26472         return cfg;
26473         
26474     },
26475     
26476     initEvents : function()
26477     {
26478         this.managerEl = this.el.select('.roo-document-manager', true).first();
26479         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26480         
26481         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26482         this.selectorEl.hide();
26483         
26484         if(this.multiple){
26485             this.selectorEl.attr('multiple', 'multiple');
26486         }
26487         
26488         this.selectorEl.on('change', this.onFileSelected, this);
26489         
26490         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26491         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26492         
26493         this.uploader.on('click', this.onUploaderClick, this);
26494         
26495         this.renderProgressDialog();
26496         
26497         var _this = this;
26498         
26499         window.addEventListener("resize", function() { _this.refresh(); } );
26500         
26501         this.fireEvent('initial', this);
26502     },
26503     
26504     renderProgressDialog : function()
26505     {
26506         var _this = this;
26507         
26508         this.progressDialog = new Roo.bootstrap.Modal({
26509             cls : 'roo-document-manager-progress-dialog',
26510             allow_close : false,
26511             title : '',
26512             buttons : [
26513                 {
26514                     name  :'cancel',
26515                     weight : 'danger',
26516                     html : 'Cancel'
26517                 }
26518             ], 
26519             listeners : { 
26520                 btnclick : function() {
26521                     _this.uploadCancel();
26522                     this.hide();
26523                 }
26524             }
26525         });
26526          
26527         this.progressDialog.render(Roo.get(document.body));
26528          
26529         this.progress = new Roo.bootstrap.Progress({
26530             cls : 'roo-document-manager-progress',
26531             active : true,
26532             striped : true
26533         });
26534         
26535         this.progress.render(this.progressDialog.getChildContainer());
26536         
26537         this.progressBar = new Roo.bootstrap.ProgressBar({
26538             cls : 'roo-document-manager-progress-bar',
26539             aria_valuenow : 0,
26540             aria_valuemin : 0,
26541             aria_valuemax : 12,
26542             panel : 'success'
26543         });
26544         
26545         this.progressBar.render(this.progress.getChildContainer());
26546     },
26547     
26548     onUploaderClick : function(e)
26549     {
26550         e.preventDefault();
26551      
26552         if(this.fireEvent('beforeselectfile', this) != false){
26553             this.selectorEl.dom.click();
26554         }
26555         
26556     },
26557     
26558     onFileSelected : function(e)
26559     {
26560         e.preventDefault();
26561         
26562         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26563             return;
26564         }
26565         
26566         Roo.each(this.selectorEl.dom.files, function(file){
26567             if(this.fireEvent('inspect', this, file) != false){
26568                 this.files.push(file);
26569             }
26570         }, this);
26571         
26572         this.queue();
26573         
26574     },
26575     
26576     queue : function()
26577     {
26578         this.selectorEl.dom.value = '';
26579         
26580         if(!this.files.length){
26581             return;
26582         }
26583         
26584         if(this.boxes > 0 && this.files.length > this.boxes){
26585             this.files = this.files.slice(0, this.boxes);
26586         }
26587         
26588         this.uploader.show();
26589         
26590         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26591             this.uploader.hide();
26592         }
26593         
26594         var _this = this;
26595         
26596         var files = [];
26597         
26598         var docs = [];
26599         
26600         Roo.each(this.files, function(file){
26601             
26602             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26603                 var f = this.renderPreview(file);
26604                 files.push(f);
26605                 return;
26606             }
26607             
26608             if(file.type.indexOf('image') != -1){
26609                 this.delegates.push(
26610                     (function(){
26611                         _this.process(file);
26612                     }).createDelegate(this)
26613                 );
26614         
26615                 return;
26616             }
26617             
26618             docs.push(
26619                 (function(){
26620                     _this.process(file);
26621                 }).createDelegate(this)
26622             );
26623             
26624         }, this);
26625         
26626         this.files = files;
26627         
26628         this.delegates = this.delegates.concat(docs);
26629         
26630         if(!this.delegates.length){
26631             this.refresh();
26632             return;
26633         }
26634         
26635         this.progressBar.aria_valuemax = this.delegates.length;
26636         
26637         this.arrange();
26638         
26639         return;
26640     },
26641     
26642     arrange : function()
26643     {
26644         if(!this.delegates.length){
26645             this.progressDialog.hide();
26646             this.refresh();
26647             return;
26648         }
26649         
26650         var delegate = this.delegates.shift();
26651         
26652         this.progressDialog.show();
26653         
26654         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26655         
26656         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26657         
26658         delegate();
26659     },
26660     
26661     refresh : function()
26662     {
26663         this.uploader.show();
26664         
26665         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26666             this.uploader.hide();
26667         }
26668         
26669         Roo.isTouch ? this.closable(false) : this.closable(true);
26670         
26671         this.fireEvent('refresh', this);
26672     },
26673     
26674     onRemove : function(e, el, o)
26675     {
26676         e.preventDefault();
26677         
26678         this.fireEvent('remove', this, o);
26679         
26680     },
26681     
26682     remove : function(o)
26683     {
26684         var files = [];
26685         
26686         Roo.each(this.files, function(file){
26687             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26688                 files.push(file);
26689                 return;
26690             }
26691
26692             o.target.remove();
26693
26694         }, this);
26695         
26696         this.files = files;
26697         
26698         this.refresh();
26699     },
26700     
26701     clear : function()
26702     {
26703         Roo.each(this.files, function(file){
26704             if(!file.target){
26705                 return;
26706             }
26707             
26708             file.target.remove();
26709
26710         }, this);
26711         
26712         this.files = [];
26713         
26714         this.refresh();
26715     },
26716     
26717     onClick : function(e, el, o)
26718     {
26719         e.preventDefault();
26720         
26721         this.fireEvent('click', this, o);
26722         
26723     },
26724     
26725     closable : function(closable)
26726     {
26727         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26728             
26729             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26730             
26731             if(closable){
26732                 el.show();
26733                 return;
26734             }
26735             
26736             el.hide();
26737             
26738         }, this);
26739     },
26740     
26741     xhrOnLoad : function(xhr)
26742     {
26743         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26744             el.remove();
26745         }, this);
26746         
26747         if (xhr.readyState !== 4) {
26748             this.arrange();
26749             this.fireEvent('exception', this, xhr);
26750             return;
26751         }
26752
26753         var response = Roo.decode(xhr.responseText);
26754         
26755         if(!response.success){
26756             this.arrange();
26757             this.fireEvent('exception', this, xhr);
26758             return;
26759         }
26760         
26761         var file = this.renderPreview(response.data);
26762         
26763         this.files.push(file);
26764         
26765         this.arrange();
26766         
26767     },
26768     
26769     xhrOnError : function(xhr)
26770     {
26771         Roo.log('xhr on error');
26772         
26773         var response = Roo.decode(xhr.responseText);
26774           
26775         Roo.log(response);
26776         
26777         this.arrange();
26778     },
26779     
26780     process : function(file)
26781     {
26782         if(this.fireEvent('process', this, file) !== false){
26783             if(this.editable && file.type.indexOf('image') != -1){
26784                 this.fireEvent('edit', this, file);
26785                 return;
26786             }
26787
26788             this.uploadStart(file, false);
26789
26790             return;
26791         }
26792         
26793     },
26794     
26795     uploadStart : function(file, crop)
26796     {
26797         this.xhr = new XMLHttpRequest();
26798         
26799         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26800             this.arrange();
26801             return;
26802         }
26803         
26804         file.xhr = this.xhr;
26805             
26806         this.managerEl.createChild({
26807             tag : 'div',
26808             cls : 'roo-document-manager-loading',
26809             cn : [
26810                 {
26811                     tag : 'div',
26812                     tooltip : file.name,
26813                     cls : 'roo-document-manager-thumb',
26814                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26815                 }
26816             ]
26817
26818         });
26819
26820         this.xhr.open(this.method, this.url, true);
26821         
26822         var headers = {
26823             "Accept": "application/json",
26824             "Cache-Control": "no-cache",
26825             "X-Requested-With": "XMLHttpRequest"
26826         };
26827         
26828         for (var headerName in headers) {
26829             var headerValue = headers[headerName];
26830             if (headerValue) {
26831                 this.xhr.setRequestHeader(headerName, headerValue);
26832             }
26833         }
26834         
26835         var _this = this;
26836         
26837         this.xhr.onload = function()
26838         {
26839             _this.xhrOnLoad(_this.xhr);
26840         }
26841         
26842         this.xhr.onerror = function()
26843         {
26844             _this.xhrOnError(_this.xhr);
26845         }
26846         
26847         var formData = new FormData();
26848
26849         formData.append('returnHTML', 'NO');
26850         
26851         if(crop){
26852             formData.append('crop', crop);
26853         }
26854         
26855         formData.append(this.paramName, file, file.name);
26856         
26857         if(this.fireEvent('prepare', this, formData) != false){
26858             this.xhr.send(formData);
26859         };
26860     },
26861     
26862     uploadCancel : function()
26863     {
26864         if (this.xhr) {
26865             this.xhr.abort();
26866         }
26867         
26868         
26869         this.delegates = [];
26870         
26871         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26872             el.remove();
26873         }, this);
26874         
26875         this.arrange();
26876     },
26877     
26878     renderPreview : function(file)
26879     {
26880         if(typeof(file.target) != 'undefined' && file.target){
26881             return file;
26882         }
26883         
26884         var previewEl = this.managerEl.createChild({
26885             tag : 'div',
26886             cls : 'roo-document-manager-preview',
26887             cn : [
26888                 {
26889                     tag : 'div',
26890                     tooltip : file.filename,
26891                     cls : 'roo-document-manager-thumb',
26892                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26893                 },
26894                 {
26895                     tag : 'button',
26896                     cls : 'close',
26897                     html : '<i class="fa fa-times-circle"></i>'
26898                 }
26899             ]
26900         });
26901
26902         var close = previewEl.select('button.close', true).first();
26903
26904         close.on('click', this.onRemove, this, file);
26905
26906         file.target = previewEl;
26907
26908         var image = previewEl.select('img', true).first();
26909         
26910         var _this = this;
26911         
26912         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26913         
26914         image.on('click', this.onClick, this, file);
26915         
26916         return file;
26917         
26918     },
26919     
26920     onPreviewLoad : function(file, image)
26921     {
26922         if(typeof(file.target) == 'undefined' || !file.target){
26923             return;
26924         }
26925         
26926         var width = image.dom.naturalWidth || image.dom.width;
26927         var height = image.dom.naturalHeight || image.dom.height;
26928         
26929         if(width > height){
26930             file.target.addClass('wide');
26931             return;
26932         }
26933         
26934         file.target.addClass('tall');
26935         return;
26936         
26937     },
26938     
26939     uploadFromSource : function(file, crop)
26940     {
26941         this.xhr = new XMLHttpRequest();
26942         
26943         this.managerEl.createChild({
26944             tag : 'div',
26945             cls : 'roo-document-manager-loading',
26946             cn : [
26947                 {
26948                     tag : 'div',
26949                     tooltip : file.name,
26950                     cls : 'roo-document-manager-thumb',
26951                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26952                 }
26953             ]
26954
26955         });
26956
26957         this.xhr.open(this.method, this.url, true);
26958         
26959         var headers = {
26960             "Accept": "application/json",
26961             "Cache-Control": "no-cache",
26962             "X-Requested-With": "XMLHttpRequest"
26963         };
26964         
26965         for (var headerName in headers) {
26966             var headerValue = headers[headerName];
26967             if (headerValue) {
26968                 this.xhr.setRequestHeader(headerName, headerValue);
26969             }
26970         }
26971         
26972         var _this = this;
26973         
26974         this.xhr.onload = function()
26975         {
26976             _this.xhrOnLoad(_this.xhr);
26977         }
26978         
26979         this.xhr.onerror = function()
26980         {
26981             _this.xhrOnError(_this.xhr);
26982         }
26983         
26984         var formData = new FormData();
26985
26986         formData.append('returnHTML', 'NO');
26987         
26988         formData.append('crop', crop);
26989         
26990         if(typeof(file.filename) != 'undefined'){
26991             formData.append('filename', file.filename);
26992         }
26993         
26994         if(typeof(file.mimetype) != 'undefined'){
26995             formData.append('mimetype', file.mimetype);
26996         }
26997         
26998         if(this.fireEvent('prepare', this, formData) != false){
26999             this.xhr.send(formData);
27000         };
27001     }
27002 });
27003
27004 /*
27005 * Licence: LGPL
27006 */
27007
27008 /**
27009  * @class Roo.bootstrap.DocumentViewer
27010  * @extends Roo.bootstrap.Component
27011  * Bootstrap DocumentViewer class
27012  * 
27013  * @constructor
27014  * Create a new DocumentViewer
27015  * @param {Object} config The config object
27016  */
27017
27018 Roo.bootstrap.DocumentViewer = function(config){
27019     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27020     
27021     this.addEvents({
27022         /**
27023          * @event initial
27024          * Fire after initEvent
27025          * @param {Roo.bootstrap.DocumentViewer} this
27026          */
27027         "initial" : true,
27028         /**
27029          * @event click
27030          * Fire after click
27031          * @param {Roo.bootstrap.DocumentViewer} this
27032          */
27033         "click" : true,
27034         /**
27035          * @event trash
27036          * Fire after trash button
27037          * @param {Roo.bootstrap.DocumentViewer} this
27038          */
27039         "trash" : true
27040         
27041     });
27042 };
27043
27044 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27045     
27046     getAutoCreate : function()
27047     {
27048         var cfg = {
27049             tag : 'div',
27050             cls : 'roo-document-viewer',
27051             cn : [
27052                 {
27053                     tag : 'div',
27054                     cls : 'roo-document-viewer-body',
27055                     cn : [
27056                         {
27057                             tag : 'div',
27058                             cls : 'roo-document-viewer-thumb',
27059                             cn : [
27060                                 {
27061                                     tag : 'img',
27062                                     cls : 'roo-document-viewer-image'
27063                                 }
27064                             ]
27065                         }
27066                     ]
27067                 },
27068                 {
27069                     tag : 'div',
27070                     cls : 'roo-document-viewer-footer',
27071                     cn : {
27072                         tag : 'div',
27073                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27074                         cn : [
27075                             {
27076                                 tag : 'div',
27077                                 cls : 'btn-group',
27078                                 cn : [
27079                                     {
27080                                         tag : 'button',
27081                                         cls : 'btn btn-default roo-document-viewer-trash',
27082                                         html : '<i class="fa fa-trash"></i>'
27083                                     }
27084                                 ]
27085                             }
27086                         ]
27087                     }
27088                 }
27089             ]
27090         };
27091         
27092         return cfg;
27093     },
27094     
27095     initEvents : function()
27096     {
27097         
27098         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27099         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27100         
27101         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27102         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27103         
27104         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27105         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27106         
27107         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27108         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27109         
27110         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27111         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27112         
27113         this.bodyEl.on('click', this.onClick, this);
27114         
27115         this.trashBtn.on('click', this.onTrash, this);
27116         
27117     },
27118     
27119     initial : function()
27120     {
27121 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27122         
27123         
27124         this.fireEvent('initial', this);
27125         
27126     },
27127     
27128     onClick : function(e)
27129     {
27130         e.preventDefault();
27131         
27132         this.fireEvent('click', this);
27133     },
27134     
27135     onTrash : function(e)
27136     {
27137         e.preventDefault();
27138         
27139         this.fireEvent('trash', this);
27140     }
27141     
27142 });
27143 /*
27144  * - LGPL
27145  *
27146  * nav progress bar
27147  * 
27148  */
27149
27150 /**
27151  * @class Roo.bootstrap.NavProgressBar
27152  * @extends Roo.bootstrap.Component
27153  * Bootstrap NavProgressBar class
27154  * 
27155  * @constructor
27156  * Create a new nav progress bar
27157  * @param {Object} config The config object
27158  */
27159
27160 Roo.bootstrap.NavProgressBar = function(config){
27161     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27162
27163     this.bullets = this.bullets || [];
27164    
27165 //    Roo.bootstrap.NavProgressBar.register(this);
27166      this.addEvents({
27167         /**
27168              * @event changed
27169              * Fires when the active item changes
27170              * @param {Roo.bootstrap.NavProgressBar} this
27171              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27172              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27173          */
27174         'changed': true
27175      });
27176     
27177 };
27178
27179 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27180     
27181     bullets : [],
27182     barItems : [],
27183     
27184     getAutoCreate : function()
27185     {
27186         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27187         
27188         cfg = {
27189             tag : 'div',
27190             cls : 'roo-navigation-bar-group',
27191             cn : [
27192                 {
27193                     tag : 'div',
27194                     cls : 'roo-navigation-top-bar'
27195                 },
27196                 {
27197                     tag : 'div',
27198                     cls : 'roo-navigation-bullets-bar',
27199                     cn : [
27200                         {
27201                             tag : 'ul',
27202                             cls : 'roo-navigation-bar'
27203                         }
27204                     ]
27205                 },
27206                 
27207                 {
27208                     tag : 'div',
27209                     cls : 'roo-navigation-bottom-bar'
27210                 }
27211             ]
27212             
27213         };
27214         
27215         return cfg;
27216         
27217     },
27218     
27219     initEvents: function() 
27220     {
27221         
27222     },
27223     
27224     onRender : function(ct, position) 
27225     {
27226         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27227         
27228         if(this.bullets.length){
27229             Roo.each(this.bullets, function(b){
27230                this.addItem(b);
27231             }, this);
27232         }
27233         
27234         this.format();
27235         
27236     },
27237     
27238     addItem : function(cfg)
27239     {
27240         var item = new Roo.bootstrap.NavProgressItem(cfg);
27241         
27242         item.parentId = this.id;
27243         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27244         
27245         if(cfg.html){
27246             var top = new Roo.bootstrap.Element({
27247                 tag : 'div',
27248                 cls : 'roo-navigation-bar-text'
27249             });
27250             
27251             var bottom = new Roo.bootstrap.Element({
27252                 tag : 'div',
27253                 cls : 'roo-navigation-bar-text'
27254             });
27255             
27256             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27257             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27258             
27259             var topText = new Roo.bootstrap.Element({
27260                 tag : 'span',
27261                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27262             });
27263             
27264             var bottomText = new Roo.bootstrap.Element({
27265                 tag : 'span',
27266                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27267             });
27268             
27269             topText.onRender(top.el, null);
27270             bottomText.onRender(bottom.el, null);
27271             
27272             item.topEl = top;
27273             item.bottomEl = bottom;
27274         }
27275         
27276         this.barItems.push(item);
27277         
27278         return item;
27279     },
27280     
27281     getActive : function()
27282     {
27283         var active = false;
27284         
27285         Roo.each(this.barItems, function(v){
27286             
27287             if (!v.isActive()) {
27288                 return;
27289             }
27290             
27291             active = v;
27292             return false;
27293             
27294         });
27295         
27296         return active;
27297     },
27298     
27299     setActiveItem : function(item)
27300     {
27301         var prev = false;
27302         
27303         Roo.each(this.barItems, function(v){
27304             if (v.rid == item.rid) {
27305                 return ;
27306             }
27307             
27308             if (v.isActive()) {
27309                 v.setActive(false);
27310                 prev = v;
27311             }
27312         });
27313
27314         item.setActive(true);
27315         
27316         this.fireEvent('changed', this, item, prev);
27317     },
27318     
27319     getBarItem: function(rid)
27320     {
27321         var ret = false;
27322         
27323         Roo.each(this.barItems, function(e) {
27324             if (e.rid != rid) {
27325                 return;
27326             }
27327             
27328             ret =  e;
27329             return false;
27330         });
27331         
27332         return ret;
27333     },
27334     
27335     indexOfItem : function(item)
27336     {
27337         var index = false;
27338         
27339         Roo.each(this.barItems, function(v, i){
27340             
27341             if (v.rid != item.rid) {
27342                 return;
27343             }
27344             
27345             index = i;
27346             return false
27347         });
27348         
27349         return index;
27350     },
27351     
27352     setActiveNext : function()
27353     {
27354         var i = this.indexOfItem(this.getActive());
27355         
27356         if (i > this.barItems.length) {
27357             return;
27358         }
27359         
27360         this.setActiveItem(this.barItems[i+1]);
27361     },
27362     
27363     setActivePrev : function()
27364     {
27365         var i = this.indexOfItem(this.getActive());
27366         
27367         if (i  < 1) {
27368             return;
27369         }
27370         
27371         this.setActiveItem(this.barItems[i-1]);
27372     },
27373     
27374     format : function()
27375     {
27376         if(!this.barItems.length){
27377             return;
27378         }
27379      
27380         var width = 100 / this.barItems.length;
27381         
27382         Roo.each(this.barItems, function(i){
27383             i.el.setStyle('width', width + '%');
27384             i.topEl.el.setStyle('width', width + '%');
27385             i.bottomEl.el.setStyle('width', width + '%');
27386         }, this);
27387         
27388     }
27389     
27390 });
27391 /*
27392  * - LGPL
27393  *
27394  * Nav Progress Item
27395  * 
27396  */
27397
27398 /**
27399  * @class Roo.bootstrap.NavProgressItem
27400  * @extends Roo.bootstrap.Component
27401  * Bootstrap NavProgressItem class
27402  * @cfg {String} rid the reference id
27403  * @cfg {Boolean} active (true|false) Is item active default false
27404  * @cfg {Boolean} disabled (true|false) Is item active default false
27405  * @cfg {String} html
27406  * @cfg {String} position (top|bottom) text position default bottom
27407  * @cfg {String} icon show icon instead of number
27408  * 
27409  * @constructor
27410  * Create a new NavProgressItem
27411  * @param {Object} config The config object
27412  */
27413 Roo.bootstrap.NavProgressItem = function(config){
27414     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27415     this.addEvents({
27416         // raw events
27417         /**
27418          * @event click
27419          * The raw click event for the entire grid.
27420          * @param {Roo.bootstrap.NavProgressItem} this
27421          * @param {Roo.EventObject} e
27422          */
27423         "click" : true
27424     });
27425    
27426 };
27427
27428 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27429     
27430     rid : '',
27431     active : false,
27432     disabled : false,
27433     html : '',
27434     position : 'bottom',
27435     icon : false,
27436     
27437     getAutoCreate : function()
27438     {
27439         var iconCls = 'roo-navigation-bar-item-icon';
27440         
27441         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27442         
27443         var cfg = {
27444             tag: 'li',
27445             cls: 'roo-navigation-bar-item',
27446             cn : [
27447                 {
27448                     tag : 'i',
27449                     cls : iconCls
27450                 }
27451             ]
27452         };
27453         
27454         if(this.active){
27455             cfg.cls += ' active';
27456         }
27457         if(this.disabled){
27458             cfg.cls += ' disabled';
27459         }
27460         
27461         return cfg;
27462     },
27463     
27464     disable : function()
27465     {
27466         this.setDisabled(true);
27467     },
27468     
27469     enable : function()
27470     {
27471         this.setDisabled(false);
27472     },
27473     
27474     initEvents: function() 
27475     {
27476         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27477         
27478         this.iconEl.on('click', this.onClick, this);
27479     },
27480     
27481     onClick : function(e)
27482     {
27483         e.preventDefault();
27484         
27485         if(this.disabled){
27486             return;
27487         }
27488         
27489         if(this.fireEvent('click', this, e) === false){
27490             return;
27491         };
27492         
27493         this.parent().setActiveItem(this);
27494     },
27495     
27496     isActive: function () 
27497     {
27498         return this.active;
27499     },
27500     
27501     setActive : function(state)
27502     {
27503         if(this.active == state){
27504             return;
27505         }
27506         
27507         this.active = state;
27508         
27509         if (state) {
27510             this.el.addClass('active');
27511             return;
27512         }
27513         
27514         this.el.removeClass('active');
27515         
27516         return;
27517     },
27518     
27519     setDisabled : function(state)
27520     {
27521         if(this.disabled == state){
27522             return;
27523         }
27524         
27525         this.disabled = state;
27526         
27527         if (state) {
27528             this.el.addClass('disabled');
27529             return;
27530         }
27531         
27532         this.el.removeClass('disabled');
27533     },
27534     
27535     tooltipEl : function()
27536     {
27537         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27538     }
27539 });
27540  
27541
27542  /*
27543  * - LGPL
27544  *
27545  * FieldLabel
27546  * 
27547  */
27548
27549 /**
27550  * @class Roo.bootstrap.FieldLabel
27551  * @extends Roo.bootstrap.Component
27552  * Bootstrap FieldLabel class
27553  * @cfg {String} html contents of the element
27554  * @cfg {String} tag tag of the element default label
27555  * @cfg {String} cls class of the element
27556  * @cfg {String} target label target 
27557  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27558  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27559  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27560  * @cfg {String} iconTooltip default "This field is required"
27561  * 
27562  * @constructor
27563  * Create a new FieldLabel
27564  * @param {Object} config The config object
27565  */
27566
27567 Roo.bootstrap.FieldLabel = function(config){
27568     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27569     
27570     this.addEvents({
27571             /**
27572              * @event invalid
27573              * Fires after the field has been marked as invalid.
27574              * @param {Roo.form.FieldLabel} this
27575              * @param {String} msg The validation message
27576              */
27577             invalid : true,
27578             /**
27579              * @event valid
27580              * Fires after the field has been validated with no errors.
27581              * @param {Roo.form.FieldLabel} this
27582              */
27583             valid : true
27584         });
27585 };
27586
27587 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27588     
27589     tag: 'label',
27590     cls: '',
27591     html: '',
27592     target: '',
27593     allowBlank : true,
27594     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27595     validClass : 'text-success fa fa-lg fa-check',
27596     iconTooltip : 'This field is required',
27597     
27598     getAutoCreate : function(){
27599         
27600         var cfg = {
27601             tag : this.tag,
27602             cls : 'roo-bootstrap-field-label ' + this.cls,
27603             for : this.target,
27604             cn : [
27605                 {
27606                     tag : 'i',
27607                     cls : '',
27608                     tooltip : this.iconTooltip
27609                 },
27610                 {
27611                     tag : 'span',
27612                     html : this.html
27613                 }
27614             ] 
27615         };
27616         
27617         return cfg;
27618     },
27619     
27620     initEvents: function() 
27621     {
27622         Roo.bootstrap.Element.superclass.initEvents.call(this);
27623         
27624         this.iconEl = this.el.select('i', true).first();
27625         
27626         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27627         
27628         Roo.bootstrap.FieldLabel.register(this);
27629     },
27630     
27631     /**
27632      * Mark this field as valid
27633      */
27634     markValid : function()
27635     {
27636         this.iconEl.show();
27637         
27638         this.iconEl.removeClass(this.invalidClass);
27639         
27640         this.iconEl.addClass(this.validClass);
27641         
27642         this.fireEvent('valid', this);
27643     },
27644     
27645     /**
27646      * Mark this field as invalid
27647      * @param {String} msg The validation message
27648      */
27649     markInvalid : function(msg)
27650     {
27651         this.iconEl.show();
27652         
27653         this.iconEl.removeClass(this.validClass);
27654         
27655         this.iconEl.addClass(this.invalidClass);
27656         
27657         this.fireEvent('invalid', this, msg);
27658     }
27659     
27660    
27661 });
27662
27663 Roo.apply(Roo.bootstrap.FieldLabel, {
27664     
27665     groups: {},
27666     
27667      /**
27668     * register a FieldLabel Group
27669     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27670     */
27671     register : function(label)
27672     {
27673         if(this.groups.hasOwnProperty(label.target)){
27674             return;
27675         }
27676      
27677         this.groups[label.target] = label;
27678         
27679     },
27680     /**
27681     * fetch a FieldLabel Group based on the target
27682     * @param {string} target
27683     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27684     */
27685     get: function(target) {
27686         if (typeof(this.groups[target]) == 'undefined') {
27687             return false;
27688         }
27689         
27690         return this.groups[target] ;
27691     }
27692 });
27693
27694  
27695
27696  /*
27697  * - LGPL
27698  *
27699  * page DateSplitField.
27700  * 
27701  */
27702
27703
27704 /**
27705  * @class Roo.bootstrap.DateSplitField
27706  * @extends Roo.bootstrap.Component
27707  * Bootstrap DateSplitField class
27708  * @cfg {string} fieldLabel - the label associated
27709  * @cfg {Number} labelWidth set the width of label (0-12)
27710  * @cfg {String} labelAlign (top|left)
27711  * @cfg {Boolean} dayAllowBlank (true|false) default false
27712  * @cfg {Boolean} monthAllowBlank (true|false) default false
27713  * @cfg {Boolean} yearAllowBlank (true|false) default false
27714  * @cfg {string} dayPlaceholder 
27715  * @cfg {string} monthPlaceholder
27716  * @cfg {string} yearPlaceholder
27717  * @cfg {string} dayFormat default 'd'
27718  * @cfg {string} monthFormat default 'm'
27719  * @cfg {string} yearFormat default 'Y'
27720
27721  *     
27722  * @constructor
27723  * Create a new DateSplitField
27724  * @param {Object} config The config object
27725  */
27726
27727 Roo.bootstrap.DateSplitField = function(config){
27728     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27729     
27730     this.addEvents({
27731         // raw events
27732          /**
27733          * @event years
27734          * getting the data of years
27735          * @param {Roo.bootstrap.DateSplitField} this
27736          * @param {Object} years
27737          */
27738         "years" : true,
27739         /**
27740          * @event days
27741          * getting the data of days
27742          * @param {Roo.bootstrap.DateSplitField} this
27743          * @param {Object} days
27744          */
27745         "days" : true,
27746         /**
27747          * @event invalid
27748          * Fires after the field has been marked as invalid.
27749          * @param {Roo.form.Field} this
27750          * @param {String} msg The validation message
27751          */
27752         invalid : true,
27753        /**
27754          * @event valid
27755          * Fires after the field has been validated with no errors.
27756          * @param {Roo.form.Field} this
27757          */
27758         valid : true
27759     });
27760 };
27761
27762 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27763     
27764     fieldLabel : '',
27765     labelAlign : 'top',
27766     labelWidth : 3,
27767     dayAllowBlank : false,
27768     monthAllowBlank : false,
27769     yearAllowBlank : false,
27770     dayPlaceholder : '',
27771     monthPlaceholder : '',
27772     yearPlaceholder : '',
27773     dayFormat : 'd',
27774     monthFormat : 'm',
27775     yearFormat : 'Y',
27776     isFormField : true,
27777     
27778     getAutoCreate : function()
27779     {
27780         var cfg = {
27781             tag : 'div',
27782             cls : 'row roo-date-split-field-group',
27783             cn : [
27784                 {
27785                     tag : 'input',
27786                     type : 'hidden',
27787                     cls : 'form-hidden-field roo-date-split-field-group-value',
27788                     name : this.name
27789                 }
27790             ]
27791         };
27792         
27793         if(this.fieldLabel){
27794             cfg.cn.push({
27795                 tag : 'div',
27796                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27797                 cn : [
27798                     {
27799                         tag : 'label',
27800                         html : this.fieldLabel
27801                     }
27802                 ]
27803             });
27804         }
27805         
27806         Roo.each(['day', 'month', 'year'], function(t){
27807             cfg.cn.push({
27808                 tag : 'div',
27809                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27810             });
27811         }, this);
27812         
27813         return cfg;
27814     },
27815     
27816     inputEl: function ()
27817     {
27818         return this.el.select('.roo-date-split-field-group-value', true).first();
27819     },
27820     
27821     onRender : function(ct, position) 
27822     {
27823         var _this = this;
27824         
27825         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27826         
27827         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27828         
27829         this.dayField = new Roo.bootstrap.ComboBox({
27830             allowBlank : this.dayAllowBlank,
27831             alwaysQuery : true,
27832             displayField : 'value',
27833             editable : false,
27834             fieldLabel : '',
27835             forceSelection : true,
27836             mode : 'local',
27837             placeholder : this.dayPlaceholder,
27838             selectOnFocus : true,
27839             tpl : '<div class="select2-result"><b>{value}</b></div>',
27840             triggerAction : 'all',
27841             typeAhead : true,
27842             valueField : 'value',
27843             store : new Roo.data.SimpleStore({
27844                 data : (function() {    
27845                     var days = [];
27846                     _this.fireEvent('days', _this, days);
27847                     return days;
27848                 })(),
27849                 fields : [ 'value' ]
27850             }),
27851             listeners : {
27852                 select : function (_self, record, index)
27853                 {
27854                     _this.setValue(_this.getValue());
27855                 }
27856             }
27857         });
27858
27859         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27860         
27861         this.monthField = new Roo.bootstrap.MonthField({
27862             after : '<i class=\"fa fa-calendar\"></i>',
27863             allowBlank : this.monthAllowBlank,
27864             placeholder : this.monthPlaceholder,
27865             readOnly : true,
27866             listeners : {
27867                 render : function (_self)
27868                 {
27869                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27870                         e.preventDefault();
27871                         _self.focus();
27872                     });
27873                 },
27874                 select : function (_self, oldvalue, newvalue)
27875                 {
27876                     _this.setValue(_this.getValue());
27877                 }
27878             }
27879         });
27880         
27881         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27882         
27883         this.yearField = new Roo.bootstrap.ComboBox({
27884             allowBlank : this.yearAllowBlank,
27885             alwaysQuery : true,
27886             displayField : 'value',
27887             editable : false,
27888             fieldLabel : '',
27889             forceSelection : true,
27890             mode : 'local',
27891             placeholder : this.yearPlaceholder,
27892             selectOnFocus : true,
27893             tpl : '<div class="select2-result"><b>{value}</b></div>',
27894             triggerAction : 'all',
27895             typeAhead : true,
27896             valueField : 'value',
27897             store : new Roo.data.SimpleStore({
27898                 data : (function() {
27899                     var years = [];
27900                     _this.fireEvent('years', _this, years);
27901                     return years;
27902                 })(),
27903                 fields : [ 'value' ]
27904             }),
27905             listeners : {
27906                 select : function (_self, record, index)
27907                 {
27908                     _this.setValue(_this.getValue());
27909                 }
27910             }
27911         });
27912
27913         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27914     },
27915     
27916     setValue : function(v, format)
27917     {
27918         this.inputEl.dom.value = v;
27919         
27920         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27921         
27922         var d = Date.parseDate(v, f);
27923         
27924         if(!d){
27925             this.validate();
27926             return;
27927         }
27928         
27929         this.setDay(d.format(this.dayFormat));
27930         this.setMonth(d.format(this.monthFormat));
27931         this.setYear(d.format(this.yearFormat));
27932         
27933         this.validate();
27934         
27935         return;
27936     },
27937     
27938     setDay : function(v)
27939     {
27940         this.dayField.setValue(v);
27941         this.inputEl.dom.value = this.getValue();
27942         this.validate();
27943         return;
27944     },
27945     
27946     setMonth : function(v)
27947     {
27948         this.monthField.setValue(v, true);
27949         this.inputEl.dom.value = this.getValue();
27950         this.validate();
27951         return;
27952     },
27953     
27954     setYear : function(v)
27955     {
27956         this.yearField.setValue(v);
27957         this.inputEl.dom.value = this.getValue();
27958         this.validate();
27959         return;
27960     },
27961     
27962     getDay : function()
27963     {
27964         return this.dayField.getValue();
27965     },
27966     
27967     getMonth : function()
27968     {
27969         return this.monthField.getValue();
27970     },
27971     
27972     getYear : function()
27973     {
27974         return this.yearField.getValue();
27975     },
27976     
27977     getValue : function()
27978     {
27979         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27980         
27981         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27982         
27983         return date;
27984     },
27985     
27986     reset : function()
27987     {
27988         this.setDay('');
27989         this.setMonth('');
27990         this.setYear('');
27991         this.inputEl.dom.value = '';
27992         this.validate();
27993         return;
27994     },
27995     
27996     validate : function()
27997     {
27998         var d = this.dayField.validate();
27999         var m = this.monthField.validate();
28000         var y = this.yearField.validate();
28001         
28002         var valid = true;
28003         
28004         if(
28005                 (!this.dayAllowBlank && !d) ||
28006                 (!this.monthAllowBlank && !m) ||
28007                 (!this.yearAllowBlank && !y)
28008         ){
28009             valid = false;
28010         }
28011         
28012         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28013             return valid;
28014         }
28015         
28016         if(valid){
28017             this.markValid();
28018             return valid;
28019         }
28020         
28021         this.markInvalid();
28022         
28023         return valid;
28024     },
28025     
28026     markValid : function()
28027     {
28028         
28029         var label = this.el.select('label', true).first();
28030         var icon = this.el.select('i.fa-star', true).first();
28031
28032         if(label && icon){
28033             icon.remove();
28034         }
28035         
28036         this.fireEvent('valid', this);
28037     },
28038     
28039      /**
28040      * Mark this field as invalid
28041      * @param {String} msg The validation message
28042      */
28043     markInvalid : function(msg)
28044     {
28045         
28046         var label = this.el.select('label', true).first();
28047         var icon = this.el.select('i.fa-star', true).first();
28048
28049         if(label && !icon){
28050             this.el.select('.roo-date-split-field-label', true).createChild({
28051                 tag : 'i',
28052                 cls : 'text-danger fa fa-lg fa-star',
28053                 tooltip : 'This field is required',
28054                 style : 'margin-right:5px;'
28055             }, label, true);
28056         }
28057         
28058         this.fireEvent('invalid', this, msg);
28059     },
28060     
28061     clearInvalid : function()
28062     {
28063         var label = this.el.select('label', true).first();
28064         var icon = this.el.select('i.fa-star', true).first();
28065
28066         if(label && icon){
28067             icon.remove();
28068         }
28069         
28070         this.fireEvent('valid', this);
28071     },
28072     
28073     getName: function()
28074     {
28075         return this.name;
28076     }
28077     
28078 });
28079
28080