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         this.el.removeClass(this.invalidClass);
8359         
8360         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8361             
8362             var feedback = this.el.select('.form-control-feedback', true).first();
8363             
8364             if(feedback){
8365                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8366             }
8367             
8368         }
8369         
8370         this.fireEvent('valid', this);
8371     },
8372     
8373      /**
8374      * Mark this field as valid
8375      */
8376     markValid : function()
8377     {
8378         if(!this.el  || this.preventMark){ // not rendered
8379             return;
8380         }
8381         
8382         this.el.removeClass([this.invalidClass, this.validClass]);
8383         
8384         var feedback = this.el.select('.form-control-feedback', true).first();
8385             
8386         if(feedback){
8387             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8388         }
8389
8390         if(this.disabled || this.allowBlank){
8391             return;
8392         }
8393         
8394         var formGroup = this.el.findParent('.form-group', false, true);
8395         
8396         if(formGroup){
8397             
8398             var label = formGroup.select('label', true).first();
8399             var icon = formGroup.select('i.fa-star', true).first();
8400             
8401             if(label && icon){
8402                 icon.remove();
8403             }
8404         }
8405         
8406         this.el.addClass(this.validClass);
8407         
8408         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8409             
8410             var feedback = this.el.select('.form-control-feedback', true).first();
8411             
8412             if(feedback){
8413                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8414                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8415             }
8416             
8417         }
8418         
8419         this.fireEvent('valid', this);
8420     },
8421     
8422      /**
8423      * Mark this field as invalid
8424      * @param {String} msg The validation message
8425      */
8426     markInvalid : function(msg)
8427     {
8428         if(!this.el  || this.preventMark){ // not rendered
8429             return;
8430         }
8431         
8432         this.el.removeClass([this.invalidClass, this.validClass]);
8433         
8434         var feedback = this.el.select('.form-control-feedback', true).first();
8435             
8436         if(feedback){
8437             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8438         }
8439
8440         if(this.disabled || this.allowBlank){
8441             return;
8442         }
8443         
8444         var formGroup = this.el.findParent('.form-group', false, true);
8445         
8446         if(formGroup){
8447             var label = formGroup.select('label', true).first();
8448             var icon = formGroup.select('i.fa-star', true).first();
8449
8450             if(!this.getValue().length && label && !icon){
8451                 this.el.findParent('.form-group', false, true).createChild({
8452                     tag : 'i',
8453                     cls : 'text-danger fa fa-lg fa-star',
8454                     tooltip : 'This field is required',
8455                     style : 'margin-right:5px;'
8456                 }, label, true);
8457             }
8458         }
8459         
8460         
8461         this.el.addClass(this.invalidClass);
8462         
8463         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8464             
8465             var feedback = this.el.select('.form-control-feedback', true).first();
8466             
8467             if(feedback){
8468                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8469                 
8470                 if(this.getValue().length || this.forceFeedback){
8471                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8472                 }
8473                 
8474             }
8475             
8476         }
8477         
8478         this.fireEvent('invalid', this, msg);
8479     },
8480     // private
8481     SafariOnKeyDown : function(event)
8482     {
8483         // this is a workaround for a password hang bug on chrome/ webkit.
8484         
8485         var isSelectAll = false;
8486         
8487         if(this.inputEl().dom.selectionEnd > 0){
8488             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8489         }
8490         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8491             event.preventDefault();
8492             this.setValue('');
8493             return;
8494         }
8495         
8496         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8497             
8498             event.preventDefault();
8499             // this is very hacky as keydown always get's upper case.
8500             //
8501             var cc = String.fromCharCode(event.getCharCode());
8502             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8503             
8504         }
8505     },
8506     adjustWidth : function(tag, w){
8507         tag = tag.toLowerCase();
8508         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8509             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8510                 if(tag == 'input'){
8511                     return w + 2;
8512                 }
8513                 if(tag == 'textarea'){
8514                     return w-2;
8515                 }
8516             }else if(Roo.isOpera){
8517                 if(tag == 'input'){
8518                     return w + 2;
8519                 }
8520                 if(tag == 'textarea'){
8521                     return w-2;
8522                 }
8523             }
8524         }
8525         return w;
8526     }
8527     
8528 });
8529
8530  
8531 /*
8532  * - LGPL
8533  *
8534  * Input
8535  * 
8536  */
8537
8538 /**
8539  * @class Roo.bootstrap.TextArea
8540  * @extends Roo.bootstrap.Input
8541  * Bootstrap TextArea class
8542  * @cfg {Number} cols Specifies the visible width of a text area
8543  * @cfg {Number} rows Specifies the visible number of lines in a text area
8544  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8545  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8546  * @cfg {string} html text
8547  * 
8548  * @constructor
8549  * Create a new TextArea
8550  * @param {Object} config The config object
8551  */
8552
8553 Roo.bootstrap.TextArea = function(config){
8554     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8555    
8556 };
8557
8558 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8559      
8560     cols : false,
8561     rows : 5,
8562     readOnly : false,
8563     warp : 'soft',
8564     resize : false,
8565     value: false,
8566     html: false,
8567     
8568     getAutoCreate : function(){
8569         
8570         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8571         
8572         var id = Roo.id();
8573         
8574         var cfg = {};
8575         
8576         var input =  {
8577             tag: 'textarea',
8578             id : id,
8579             warp : this.warp,
8580             rows : this.rows,
8581             value : this.value || '',
8582             html: this.html || '',
8583             cls : 'form-control',
8584             placeholder : this.placeholder || '' 
8585             
8586         };
8587         
8588         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8589             input.maxLength = this.maxLength;
8590         }
8591         
8592         if(this.resize){
8593             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8594         }
8595         
8596         if(this.cols){
8597             input.cols = this.cols;
8598         }
8599         
8600         if (this.readOnly) {
8601             input.readonly = true;
8602         }
8603         
8604         if (this.name) {
8605             input.name = this.name;
8606         }
8607         
8608         if (this.size) {
8609             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8610         }
8611         
8612         var settings=this;
8613         ['xs','sm','md','lg'].map(function(size){
8614             if (settings[size]) {
8615                 cfg.cls += ' col-' + size + '-' + settings[size];
8616             }
8617         });
8618         
8619         var inputblock = input;
8620         
8621         if(this.hasFeedback && !this.allowBlank){
8622             
8623             var feedback = {
8624                 tag: 'span',
8625                 cls: 'glyphicon form-control-feedback'
8626             };
8627
8628             inputblock = {
8629                 cls : 'has-feedback',
8630                 cn :  [
8631                     input,
8632                     feedback
8633                 ] 
8634             };  
8635         }
8636         
8637         
8638         if (this.before || this.after) {
8639             
8640             inputblock = {
8641                 cls : 'input-group',
8642                 cn :  [] 
8643             };
8644             if (this.before) {
8645                 inputblock.cn.push({
8646                     tag :'span',
8647                     cls : 'input-group-addon',
8648                     html : this.before
8649                 });
8650             }
8651             
8652             inputblock.cn.push(input);
8653             
8654             if(this.hasFeedback && !this.allowBlank){
8655                 inputblock.cls += ' has-feedback';
8656                 inputblock.cn.push(feedback);
8657             }
8658             
8659             if (this.after) {
8660                 inputblock.cn.push({
8661                     tag :'span',
8662                     cls : 'input-group-addon',
8663                     html : this.after
8664                 });
8665             }
8666             
8667         }
8668         
8669         if (align ==='left' && this.fieldLabel.length) {
8670 //                Roo.log("left and has label");
8671                 cfg.cn = [
8672                     
8673                     {
8674                         tag: 'label',
8675                         'for' :  id,
8676                         cls : 'control-label col-sm-' + this.labelWidth,
8677                         html : this.fieldLabel
8678                         
8679                     },
8680                     {
8681                         cls : "col-sm-" + (12 - this.labelWidth), 
8682                         cn: [
8683                             inputblock
8684                         ]
8685                     }
8686                     
8687                 ];
8688         } else if ( this.fieldLabel.length) {
8689 //                Roo.log(" label");
8690                  cfg.cn = [
8691                    
8692                     {
8693                         tag: 'label',
8694                         //cls : 'input-group-addon',
8695                         html : this.fieldLabel
8696                         
8697                     },
8698                     
8699                     inputblock
8700                     
8701                 ];
8702
8703         } else {
8704             
8705 //                   Roo.log(" no label && no align");
8706                 cfg.cn = [
8707                     
8708                         inputblock
8709                     
8710                 ];
8711                 
8712                 
8713         }
8714         
8715         if (this.disabled) {
8716             input.disabled=true;
8717         }
8718         
8719         return cfg;
8720         
8721     },
8722     /**
8723      * return the real textarea element.
8724      */
8725     inputEl: function ()
8726     {
8727         return this.el.select('textarea.form-control',true).first();
8728     },
8729     
8730     /**
8731      * Clear any invalid styles/messages for this field
8732      */
8733     clearInvalid : function()
8734     {
8735         
8736         if(!this.el || this.preventMark){ // not rendered
8737             return;
8738         }
8739         
8740         var label = this.el.select('label', true).first();
8741         var icon = this.el.select('i.fa-star', true).first();
8742         
8743         if(label && icon){
8744             icon.remove();
8745         }
8746         
8747         this.el.removeClass(this.invalidClass);
8748         
8749         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8750             
8751             var feedback = this.el.select('.form-control-feedback', true).first();
8752             
8753             if(feedback){
8754                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8755             }
8756             
8757         }
8758         
8759         this.fireEvent('valid', this);
8760     },
8761     
8762      /**
8763      * Mark this field as valid
8764      */
8765     markValid : function()
8766     {
8767         if(!this.el  || this.preventMark){ // not rendered
8768             return;
8769         }
8770         
8771         this.el.removeClass([this.invalidClass, this.validClass]);
8772         
8773         var feedback = this.el.select('.form-control-feedback', true).first();
8774             
8775         if(feedback){
8776             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8777         }
8778
8779         if(this.disabled || this.allowBlank){
8780             return;
8781         }
8782         
8783         var label = this.el.select('label', true).first();
8784         var icon = this.el.select('i.fa-star', true).first();
8785         
8786         if(label && icon){
8787             icon.remove();
8788         }
8789         
8790         this.el.addClass(this.validClass);
8791         
8792         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8793             
8794             var feedback = this.el.select('.form-control-feedback', true).first();
8795             
8796             if(feedback){
8797                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8798                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8799             }
8800             
8801         }
8802         
8803         this.fireEvent('valid', this);
8804     },
8805     
8806      /**
8807      * Mark this field as invalid
8808      * @param {String} msg The validation message
8809      */
8810     markInvalid : function(msg)
8811     {
8812         if(!this.el  || this.preventMark){ // not rendered
8813             return;
8814         }
8815         
8816         this.el.removeClass([this.invalidClass, this.validClass]);
8817         
8818         var feedback = this.el.select('.form-control-feedback', true).first();
8819             
8820         if(feedback){
8821             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8822         }
8823
8824         if(this.disabled || this.allowBlank){
8825             return;
8826         }
8827         
8828         var label = this.el.select('label', true).first();
8829         var icon = this.el.select('i.fa-star', true).first();
8830         
8831         if(!this.getValue().length && label && !icon){
8832             this.el.createChild({
8833                 tag : 'i',
8834                 cls : 'text-danger fa fa-lg fa-star',
8835                 tooltip : 'This field is required',
8836                 style : 'margin-right:5px;'
8837             }, label, true);
8838         }
8839
8840         this.el.addClass(this.invalidClass);
8841         
8842         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8843             
8844             var feedback = this.el.select('.form-control-feedback', true).first();
8845             
8846             if(feedback){
8847                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8848                 
8849                 if(this.getValue().length || this.forceFeedback){
8850                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8851                 }
8852                 
8853             }
8854             
8855         }
8856         
8857         this.fireEvent('invalid', this, msg);
8858     }
8859 });
8860
8861  
8862 /*
8863  * - LGPL
8864  *
8865  * trigger field - base class for combo..
8866  * 
8867  */
8868  
8869 /**
8870  * @class Roo.bootstrap.TriggerField
8871  * @extends Roo.bootstrap.Input
8872  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8873  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8874  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8875  * for which you can provide a custom implementation.  For example:
8876  * <pre><code>
8877 var trigger = new Roo.bootstrap.TriggerField();
8878 trigger.onTriggerClick = myTriggerFn;
8879 trigger.applyTo('my-field');
8880 </code></pre>
8881  *
8882  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8883  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8884  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8885  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8886  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8887
8888  * @constructor
8889  * Create a new TriggerField.
8890  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8891  * to the base TextField)
8892  */
8893 Roo.bootstrap.TriggerField = function(config){
8894     this.mimicing = false;
8895     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8896 };
8897
8898 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8899     /**
8900      * @cfg {String} triggerClass A CSS class to apply to the trigger
8901      */
8902      /**
8903      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8904      */
8905     hideTrigger:false,
8906
8907     /**
8908      * @cfg {Boolean} removable (true|false) special filter default false
8909      */
8910     removable : false,
8911     
8912     /** @cfg {Boolean} grow @hide */
8913     /** @cfg {Number} growMin @hide */
8914     /** @cfg {Number} growMax @hide */
8915
8916     /**
8917      * @hide 
8918      * @method
8919      */
8920     autoSize: Roo.emptyFn,
8921     // private
8922     monitorTab : true,
8923     // private
8924     deferHeight : true,
8925
8926     
8927     actionMode : 'wrap',
8928     
8929     caret : false,
8930     
8931     
8932     getAutoCreate : function(){
8933        
8934         var align = this.labelAlign || this.parentLabelAlign();
8935         
8936         var id = Roo.id();
8937         
8938         var cfg = {
8939             cls: 'form-group' //input-group
8940         };
8941         
8942         
8943         var input =  {
8944             tag: 'input',
8945             id : id,
8946             type : this.inputType,
8947             cls : 'form-control',
8948             autocomplete: 'new-password',
8949             placeholder : this.placeholder || '' 
8950             
8951         };
8952         if (this.name) {
8953             input.name = this.name;
8954         }
8955         if (this.size) {
8956             input.cls += ' input-' + this.size;
8957         }
8958         
8959         if (this.disabled) {
8960             input.disabled=true;
8961         }
8962         
8963         var inputblock = input;
8964         
8965         if(this.hasFeedback && !this.allowBlank){
8966             
8967             var feedback = {
8968                 tag: 'span',
8969                 cls: 'glyphicon form-control-feedback'
8970             };
8971             
8972             if(this.removable && !this.editable && !this.tickable){
8973                 inputblock = {
8974                     cls : 'has-feedback',
8975                     cn :  [
8976                         inputblock,
8977                         {
8978                             tag: 'button',
8979                             html : 'x',
8980                             cls : 'roo-combo-removable-btn close'
8981                         },
8982                         feedback
8983                     ] 
8984                 };
8985             } else {
8986                 inputblock = {
8987                     cls : 'has-feedback',
8988                     cn :  [
8989                         inputblock,
8990                         feedback
8991                     ] 
8992                 };
8993             }
8994
8995         } else {
8996             if(this.removable && !this.editable && !this.tickable){
8997                 inputblock = {
8998                     cls : 'roo-removable',
8999                     cn :  [
9000                         inputblock,
9001                         {
9002                             tag: 'button',
9003                             html : 'x',
9004                             cls : 'roo-combo-removable-btn close'
9005                         }
9006                     ] 
9007                 };
9008             }
9009         }
9010         
9011         if (this.before || this.after) {
9012             
9013             inputblock = {
9014                 cls : 'input-group',
9015                 cn :  [] 
9016             };
9017             if (this.before) {
9018                 inputblock.cn.push({
9019                     tag :'span',
9020                     cls : 'input-group-addon',
9021                     html : this.before
9022                 });
9023             }
9024             
9025             inputblock.cn.push(input);
9026             
9027             if(this.hasFeedback && !this.allowBlank){
9028                 inputblock.cls += ' has-feedback';
9029                 inputblock.cn.push(feedback);
9030             }
9031             
9032             if (this.after) {
9033                 inputblock.cn.push({
9034                     tag :'span',
9035                     cls : 'input-group-addon',
9036                     html : this.after
9037                 });
9038             }
9039             
9040         };
9041         
9042         var box = {
9043             tag: 'div',
9044             cn: [
9045                 {
9046                     tag: 'input',
9047                     type : 'hidden',
9048                     cls: 'form-hidden-field'
9049                 },
9050                 inputblock
9051             ]
9052             
9053         };
9054         
9055         if(this.multiple){
9056             box = {
9057                 tag: 'div',
9058                 cn: [
9059                     {
9060                         tag: 'input',
9061                         type : 'hidden',
9062                         cls: 'form-hidden-field'
9063                     },
9064                     {
9065                         tag: 'ul',
9066                         cls: 'select2-choices',
9067                         cn:[
9068                             {
9069                                 tag: 'li',
9070                                 cls: 'select2-search-field',
9071                                 cn: [
9072
9073                                     inputblock
9074                                 ]
9075                             }
9076                         ]
9077                     }
9078                 ]
9079             }
9080         };
9081         
9082         var combobox = {
9083             cls: 'select2-container input-group',
9084             cn: [
9085                 box
9086 //                {
9087 //                    tag: 'ul',
9088 //                    cls: 'typeahead typeahead-long dropdown-menu',
9089 //                    style: 'display:none'
9090 //                }
9091             ]
9092         };
9093         
9094         if(!this.multiple && this.showToggleBtn){
9095             
9096             var caret = {
9097                         tag: 'span',
9098                         cls: 'caret'
9099              };
9100             if (this.caret != false) {
9101                 caret = {
9102                      tag: 'i',
9103                      cls: 'fa fa-' + this.caret
9104                 };
9105                 
9106             }
9107             
9108             combobox.cn.push({
9109                 tag :'span',
9110                 cls : 'input-group-addon btn dropdown-toggle',
9111                 cn : [
9112                     caret,
9113                     {
9114                         tag: 'span',
9115                         cls: 'combobox-clear',
9116                         cn  : [
9117                             {
9118                                 tag : 'i',
9119                                 cls: 'icon-remove'
9120                             }
9121                         ]
9122                     }
9123                 ]
9124
9125             })
9126         }
9127         
9128         if(this.multiple){
9129             combobox.cls += ' select2-container-multi';
9130         }
9131         
9132         if (align ==='left' && this.fieldLabel.length) {
9133             
9134 //                Roo.log("left and has label");
9135                 cfg.cn = [
9136                     
9137                     {
9138                         tag: 'label',
9139                         'for' :  id,
9140                         cls : 'control-label col-sm-' + this.labelWidth,
9141                         html : this.fieldLabel
9142                         
9143                     },
9144                     {
9145                         cls : "col-sm-" + (12 - this.labelWidth), 
9146                         cn: [
9147                             combobox
9148                         ]
9149                     }
9150                     
9151                 ];
9152         } else if ( this.fieldLabel.length) {
9153 //                Roo.log(" label");
9154                  cfg.cn = [
9155                    
9156                     {
9157                         tag: 'label',
9158                         //cls : 'input-group-addon',
9159                         html : this.fieldLabel
9160                         
9161                     },
9162                     
9163                     combobox
9164                     
9165                 ];
9166
9167         } else {
9168             
9169 //                Roo.log(" no label && no align");
9170                 cfg = combobox
9171                      
9172                 
9173         }
9174          
9175         var settings=this;
9176         ['xs','sm','md','lg'].map(function(size){
9177             if (settings[size]) {
9178                 cfg.cls += ' col-' + size + '-' + settings[size];
9179             }
9180         });
9181         
9182         return cfg;
9183         
9184     },
9185     
9186     
9187     
9188     // private
9189     onResize : function(w, h){
9190 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9191 //        if(typeof w == 'number'){
9192 //            var x = w - this.trigger.getWidth();
9193 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9194 //            this.trigger.setStyle('left', x+'px');
9195 //        }
9196     },
9197
9198     // private
9199     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9200
9201     // private
9202     getResizeEl : function(){
9203         return this.inputEl();
9204     },
9205
9206     // private
9207     getPositionEl : function(){
9208         return this.inputEl();
9209     },
9210
9211     // private
9212     alignErrorIcon : function(){
9213         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9214     },
9215
9216     // private
9217     initEvents : function(){
9218         
9219         this.createList();
9220         
9221         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9222         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9223         if(!this.multiple && this.showToggleBtn){
9224             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9225             if(this.hideTrigger){
9226                 this.trigger.setDisplayed(false);
9227             }
9228             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9229         }
9230         
9231         if(this.multiple){
9232             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9233         }
9234         
9235         if(this.removable && !this.editable && !this.tickable){
9236             var close = this.closeTriggerEl();
9237             
9238             if(close){
9239                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9240                 close.on('click', this.removeBtnClick, this, close);
9241             }
9242         }
9243         
9244         //this.trigger.addClassOnOver('x-form-trigger-over');
9245         //this.trigger.addClassOnClick('x-form-trigger-click');
9246         
9247         //if(!this.width){
9248         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9249         //}
9250     },
9251     
9252     closeTriggerEl : function()
9253     {
9254         var close = this.el.select('.roo-combo-removable-btn', true).first();
9255         return close ? close : false;
9256     },
9257     
9258     removeBtnClick : function(e, h, el)
9259     {
9260         e.preventDefault();
9261         
9262         if(this.fireEvent("remove", this) !== false){
9263             this.reset();
9264         }
9265     },
9266     
9267     createList : function()
9268     {
9269         this.list = Roo.get(document.body).createChild({
9270             tag: 'ul',
9271             cls: 'typeahead typeahead-long dropdown-menu',
9272             style: 'display:none'
9273         });
9274         
9275         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9276         
9277     },
9278
9279     // private
9280     initTrigger : function(){
9281        
9282     },
9283
9284     // private
9285     onDestroy : function(){
9286         if(this.trigger){
9287             this.trigger.removeAllListeners();
9288           //  this.trigger.remove();
9289         }
9290         //if(this.wrap){
9291         //    this.wrap.remove();
9292         //}
9293         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9294     },
9295
9296     // private
9297     onFocus : function(){
9298         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9299         /*
9300         if(!this.mimicing){
9301             this.wrap.addClass('x-trigger-wrap-focus');
9302             this.mimicing = true;
9303             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9304             if(this.monitorTab){
9305                 this.el.on("keydown", this.checkTab, this);
9306             }
9307         }
9308         */
9309     },
9310
9311     // private
9312     checkTab : function(e){
9313         if(e.getKey() == e.TAB){
9314             this.triggerBlur();
9315         }
9316     },
9317
9318     // private
9319     onBlur : function(){
9320         // do nothing
9321     },
9322
9323     // private
9324     mimicBlur : function(e, t){
9325         /*
9326         if(!this.wrap.contains(t) && this.validateBlur()){
9327             this.triggerBlur();
9328         }
9329         */
9330     },
9331
9332     // private
9333     triggerBlur : function(){
9334         this.mimicing = false;
9335         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9336         if(this.monitorTab){
9337             this.el.un("keydown", this.checkTab, this);
9338         }
9339         //this.wrap.removeClass('x-trigger-wrap-focus');
9340         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9341     },
9342
9343     // private
9344     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9345     validateBlur : function(e, t){
9346         return true;
9347     },
9348
9349     // private
9350     onDisable : function(){
9351         this.inputEl().dom.disabled = true;
9352         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9353         //if(this.wrap){
9354         //    this.wrap.addClass('x-item-disabled');
9355         //}
9356     },
9357
9358     // private
9359     onEnable : function(){
9360         this.inputEl().dom.disabled = false;
9361         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9362         //if(this.wrap){
9363         //    this.el.removeClass('x-item-disabled');
9364         //}
9365     },
9366
9367     // private
9368     onShow : function(){
9369         var ae = this.getActionEl();
9370         
9371         if(ae){
9372             ae.dom.style.display = '';
9373             ae.dom.style.visibility = 'visible';
9374         }
9375     },
9376
9377     // private
9378     
9379     onHide : function(){
9380         var ae = this.getActionEl();
9381         ae.dom.style.display = 'none';
9382     },
9383
9384     /**
9385      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9386      * by an implementing function.
9387      * @method
9388      * @param {EventObject} e
9389      */
9390     onTriggerClick : Roo.emptyFn
9391 });
9392  /*
9393  * Based on:
9394  * Ext JS Library 1.1.1
9395  * Copyright(c) 2006-2007, Ext JS, LLC.
9396  *
9397  * Originally Released Under LGPL - original licence link has changed is not relivant.
9398  *
9399  * Fork - LGPL
9400  * <script type="text/javascript">
9401  */
9402
9403
9404 /**
9405  * @class Roo.data.SortTypes
9406  * @singleton
9407  * Defines the default sorting (casting?) comparison functions used when sorting data.
9408  */
9409 Roo.data.SortTypes = {
9410     /**
9411      * Default sort that does nothing
9412      * @param {Mixed} s The value being converted
9413      * @return {Mixed} The comparison value
9414      */
9415     none : function(s){
9416         return s;
9417     },
9418     
9419     /**
9420      * The regular expression used to strip tags
9421      * @type {RegExp}
9422      * @property
9423      */
9424     stripTagsRE : /<\/?[^>]+>/gi,
9425     
9426     /**
9427      * Strips all HTML tags to sort on text only
9428      * @param {Mixed} s The value being converted
9429      * @return {String} The comparison value
9430      */
9431     asText : function(s){
9432         return String(s).replace(this.stripTagsRE, "");
9433     },
9434     
9435     /**
9436      * Strips all HTML tags to sort on text only - Case insensitive
9437      * @param {Mixed} s The value being converted
9438      * @return {String} The comparison value
9439      */
9440     asUCText : function(s){
9441         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9442     },
9443     
9444     /**
9445      * Case insensitive string
9446      * @param {Mixed} s The value being converted
9447      * @return {String} The comparison value
9448      */
9449     asUCString : function(s) {
9450         return String(s).toUpperCase();
9451     },
9452     
9453     /**
9454      * Date sorting
9455      * @param {Mixed} s The value being converted
9456      * @return {Number} The comparison value
9457      */
9458     asDate : function(s) {
9459         if(!s){
9460             return 0;
9461         }
9462         if(s instanceof Date){
9463             return s.getTime();
9464         }
9465         return Date.parse(String(s));
9466     },
9467     
9468     /**
9469      * Float sorting
9470      * @param {Mixed} s The value being converted
9471      * @return {Float} The comparison value
9472      */
9473     asFloat : function(s) {
9474         var val = parseFloat(String(s).replace(/,/g, ""));
9475         if(isNaN(val)) {
9476             val = 0;
9477         }
9478         return val;
9479     },
9480     
9481     /**
9482      * Integer sorting
9483      * @param {Mixed} s The value being converted
9484      * @return {Number} The comparison value
9485      */
9486     asInt : function(s) {
9487         var val = parseInt(String(s).replace(/,/g, ""));
9488         if(isNaN(val)) {
9489             val = 0;
9490         }
9491         return val;
9492     }
9493 };/*
9494  * Based on:
9495  * Ext JS Library 1.1.1
9496  * Copyright(c) 2006-2007, Ext JS, LLC.
9497  *
9498  * Originally Released Under LGPL - original licence link has changed is not relivant.
9499  *
9500  * Fork - LGPL
9501  * <script type="text/javascript">
9502  */
9503
9504 /**
9505 * @class Roo.data.Record
9506  * Instances of this class encapsulate both record <em>definition</em> information, and record
9507  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9508  * to access Records cached in an {@link Roo.data.Store} object.<br>
9509  * <p>
9510  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9511  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9512  * objects.<br>
9513  * <p>
9514  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9515  * @constructor
9516  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9517  * {@link #create}. The parameters are the same.
9518  * @param {Array} data An associative Array of data values keyed by the field name.
9519  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9520  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9521  * not specified an integer id is generated.
9522  */
9523 Roo.data.Record = function(data, id){
9524     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9525     this.data = data;
9526 };
9527
9528 /**
9529  * Generate a constructor for a specific record layout.
9530  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9531  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9532  * Each field definition object may contain the following properties: <ul>
9533  * <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,
9534  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9535  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9536  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9537  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9538  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9539  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9540  * this may be omitted.</p></li>
9541  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9542  * <ul><li>auto (Default, implies no conversion)</li>
9543  * <li>string</li>
9544  * <li>int</li>
9545  * <li>float</li>
9546  * <li>boolean</li>
9547  * <li>date</li></ul></p></li>
9548  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9549  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9550  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9551  * by the Reader into an object that will be stored in the Record. It is passed the
9552  * following parameters:<ul>
9553  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9554  * </ul></p></li>
9555  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9556  * </ul>
9557  * <br>usage:<br><pre><code>
9558 var TopicRecord = Roo.data.Record.create(
9559     {name: 'title', mapping: 'topic_title'},
9560     {name: 'author', mapping: 'username'},
9561     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9562     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9563     {name: 'lastPoster', mapping: 'user2'},
9564     {name: 'excerpt', mapping: 'post_text'}
9565 );
9566
9567 var myNewRecord = new TopicRecord({
9568     title: 'Do my job please',
9569     author: 'noobie',
9570     totalPosts: 1,
9571     lastPost: new Date(),
9572     lastPoster: 'Animal',
9573     excerpt: 'No way dude!'
9574 });
9575 myStore.add(myNewRecord);
9576 </code></pre>
9577  * @method create
9578  * @static
9579  */
9580 Roo.data.Record.create = function(o){
9581     var f = function(){
9582         f.superclass.constructor.apply(this, arguments);
9583     };
9584     Roo.extend(f, Roo.data.Record);
9585     var p = f.prototype;
9586     p.fields = new Roo.util.MixedCollection(false, function(field){
9587         return field.name;
9588     });
9589     for(var i = 0, len = o.length; i < len; i++){
9590         p.fields.add(new Roo.data.Field(o[i]));
9591     }
9592     f.getField = function(name){
9593         return p.fields.get(name);  
9594     };
9595     return f;
9596 };
9597
9598 Roo.data.Record.AUTO_ID = 1000;
9599 Roo.data.Record.EDIT = 'edit';
9600 Roo.data.Record.REJECT = 'reject';
9601 Roo.data.Record.COMMIT = 'commit';
9602
9603 Roo.data.Record.prototype = {
9604     /**
9605      * Readonly flag - true if this record has been modified.
9606      * @type Boolean
9607      */
9608     dirty : false,
9609     editing : false,
9610     error: null,
9611     modified: null,
9612
9613     // private
9614     join : function(store){
9615         this.store = store;
9616     },
9617
9618     /**
9619      * Set the named field to the specified value.
9620      * @param {String} name The name of the field to set.
9621      * @param {Object} value The value to set the field to.
9622      */
9623     set : function(name, value){
9624         if(this.data[name] == value){
9625             return;
9626         }
9627         this.dirty = true;
9628         if(!this.modified){
9629             this.modified = {};
9630         }
9631         if(typeof this.modified[name] == 'undefined'){
9632             this.modified[name] = this.data[name];
9633         }
9634         this.data[name] = value;
9635         if(!this.editing && this.store){
9636             this.store.afterEdit(this);
9637         }       
9638     },
9639
9640     /**
9641      * Get the value of the named field.
9642      * @param {String} name The name of the field to get the value of.
9643      * @return {Object} The value of the field.
9644      */
9645     get : function(name){
9646         return this.data[name]; 
9647     },
9648
9649     // private
9650     beginEdit : function(){
9651         this.editing = true;
9652         this.modified = {}; 
9653     },
9654
9655     // private
9656     cancelEdit : function(){
9657         this.editing = false;
9658         delete this.modified;
9659     },
9660
9661     // private
9662     endEdit : function(){
9663         this.editing = false;
9664         if(this.dirty && this.store){
9665             this.store.afterEdit(this);
9666         }
9667     },
9668
9669     /**
9670      * Usually called by the {@link Roo.data.Store} which owns the Record.
9671      * Rejects all changes made to the Record since either creation, or the last commit operation.
9672      * Modified fields are reverted to their original values.
9673      * <p>
9674      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9675      * of reject operations.
9676      */
9677     reject : function(){
9678         var m = this.modified;
9679         for(var n in m){
9680             if(typeof m[n] != "function"){
9681                 this.data[n] = m[n];
9682             }
9683         }
9684         this.dirty = false;
9685         delete this.modified;
9686         this.editing = false;
9687         if(this.store){
9688             this.store.afterReject(this);
9689         }
9690     },
9691
9692     /**
9693      * Usually called by the {@link Roo.data.Store} which owns the Record.
9694      * Commits all changes made to the Record since either creation, or the last commit operation.
9695      * <p>
9696      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9697      * of commit operations.
9698      */
9699     commit : function(){
9700         this.dirty = false;
9701         delete this.modified;
9702         this.editing = false;
9703         if(this.store){
9704             this.store.afterCommit(this);
9705         }
9706     },
9707
9708     // private
9709     hasError : function(){
9710         return this.error != null;
9711     },
9712
9713     // private
9714     clearError : function(){
9715         this.error = null;
9716     },
9717
9718     /**
9719      * Creates a copy of this record.
9720      * @param {String} id (optional) A new record id if you don't want to use this record's id
9721      * @return {Record}
9722      */
9723     copy : function(newId) {
9724         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9725     }
9726 };/*
9727  * Based on:
9728  * Ext JS Library 1.1.1
9729  * Copyright(c) 2006-2007, Ext JS, LLC.
9730  *
9731  * Originally Released Under LGPL - original licence link has changed is not relivant.
9732  *
9733  * Fork - LGPL
9734  * <script type="text/javascript">
9735  */
9736
9737
9738
9739 /**
9740  * @class Roo.data.Store
9741  * @extends Roo.util.Observable
9742  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9743  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9744  * <p>
9745  * 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
9746  * has no knowledge of the format of the data returned by the Proxy.<br>
9747  * <p>
9748  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9749  * instances from the data object. These records are cached and made available through accessor functions.
9750  * @constructor
9751  * Creates a new Store.
9752  * @param {Object} config A config object containing the objects needed for the Store to access data,
9753  * and read the data into Records.
9754  */
9755 Roo.data.Store = function(config){
9756     this.data = new Roo.util.MixedCollection(false);
9757     this.data.getKey = function(o){
9758         return o.id;
9759     };
9760     this.baseParams = {};
9761     // private
9762     this.paramNames = {
9763         "start" : "start",
9764         "limit" : "limit",
9765         "sort" : "sort",
9766         "dir" : "dir",
9767         "multisort" : "_multisort"
9768     };
9769
9770     if(config && config.data){
9771         this.inlineData = config.data;
9772         delete config.data;
9773     }
9774
9775     Roo.apply(this, config);
9776     
9777     if(this.reader){ // reader passed
9778         this.reader = Roo.factory(this.reader, Roo.data);
9779         this.reader.xmodule = this.xmodule || false;
9780         if(!this.recordType){
9781             this.recordType = this.reader.recordType;
9782         }
9783         if(this.reader.onMetaChange){
9784             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9785         }
9786     }
9787
9788     if(this.recordType){
9789         this.fields = this.recordType.prototype.fields;
9790     }
9791     this.modified = [];
9792
9793     this.addEvents({
9794         /**
9795          * @event datachanged
9796          * Fires when the data cache has changed, and a widget which is using this Store
9797          * as a Record cache should refresh its view.
9798          * @param {Store} this
9799          */
9800         datachanged : true,
9801         /**
9802          * @event metachange
9803          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9804          * @param {Store} this
9805          * @param {Object} meta The JSON metadata
9806          */
9807         metachange : true,
9808         /**
9809          * @event add
9810          * Fires when Records have been added to the Store
9811          * @param {Store} this
9812          * @param {Roo.data.Record[]} records The array of Records added
9813          * @param {Number} index The index at which the record(s) were added
9814          */
9815         add : true,
9816         /**
9817          * @event remove
9818          * Fires when a Record has been removed from the Store
9819          * @param {Store} this
9820          * @param {Roo.data.Record} record The Record that was removed
9821          * @param {Number} index The index at which the record was removed
9822          */
9823         remove : true,
9824         /**
9825          * @event update
9826          * Fires when a Record has been updated
9827          * @param {Store} this
9828          * @param {Roo.data.Record} record The Record that was updated
9829          * @param {String} operation The update operation being performed.  Value may be one of:
9830          * <pre><code>
9831  Roo.data.Record.EDIT
9832  Roo.data.Record.REJECT
9833  Roo.data.Record.COMMIT
9834          * </code></pre>
9835          */
9836         update : true,
9837         /**
9838          * @event clear
9839          * Fires when the data cache has been cleared.
9840          * @param {Store} this
9841          */
9842         clear : true,
9843         /**
9844          * @event beforeload
9845          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9846          * the load action will be canceled.
9847          * @param {Store} this
9848          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9849          */
9850         beforeload : true,
9851         /**
9852          * @event beforeloadadd
9853          * Fires after a new set of Records has been loaded.
9854          * @param {Store} this
9855          * @param {Roo.data.Record[]} records The Records that were loaded
9856          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9857          */
9858         beforeloadadd : true,
9859         /**
9860          * @event load
9861          * Fires after a new set of Records has been loaded, before they are added to the store.
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          * @params {Object} return from reader
9866          */
9867         load : true,
9868         /**
9869          * @event loadexception
9870          * Fires if an exception occurs in the Proxy during loading.
9871          * Called with the signature of the Proxy's "loadexception" event.
9872          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9873          * 
9874          * @param {Proxy} 
9875          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9876          * @param {Object} load options 
9877          * @param {Object} jsonData from your request (normally this contains the Exception)
9878          */
9879         loadexception : true
9880     });
9881     
9882     if(this.proxy){
9883         this.proxy = Roo.factory(this.proxy, Roo.data);
9884         this.proxy.xmodule = this.xmodule || false;
9885         this.relayEvents(this.proxy,  ["loadexception"]);
9886     }
9887     this.sortToggle = {};
9888     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9889
9890     Roo.data.Store.superclass.constructor.call(this);
9891
9892     if(this.inlineData){
9893         this.loadData(this.inlineData);
9894         delete this.inlineData;
9895     }
9896 };
9897
9898 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9899      /**
9900     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9901     * without a remote query - used by combo/forms at present.
9902     */
9903     
9904     /**
9905     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9906     */
9907     /**
9908     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9909     */
9910     /**
9911     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9912     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9913     */
9914     /**
9915     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9916     * on any HTTP request
9917     */
9918     /**
9919     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9920     */
9921     /**
9922     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
9923     */
9924     multiSort: false,
9925     /**
9926     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
9927     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
9928     */
9929     remoteSort : false,
9930
9931     /**
9932     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
9933      * loaded or when a record is removed. (defaults to false).
9934     */
9935     pruneModifiedRecords : false,
9936
9937     // private
9938     lastOptions : null,
9939
9940     /**
9941      * Add Records to the Store and fires the add event.
9942      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9943      */
9944     add : function(records){
9945         records = [].concat(records);
9946         for(var i = 0, len = records.length; i < len; i++){
9947             records[i].join(this);
9948         }
9949         var index = this.data.length;
9950         this.data.addAll(records);
9951         this.fireEvent("add", this, records, index);
9952     },
9953
9954     /**
9955      * Remove a Record from the Store and fires the remove event.
9956      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
9957      */
9958     remove : function(record){
9959         var index = this.data.indexOf(record);
9960         this.data.removeAt(index);
9961         if(this.pruneModifiedRecords){
9962             this.modified.remove(record);
9963         }
9964         this.fireEvent("remove", this, record, index);
9965     },
9966
9967     /**
9968      * Remove all Records from the Store and fires the clear event.
9969      */
9970     removeAll : function(){
9971         this.data.clear();
9972         if(this.pruneModifiedRecords){
9973             this.modified = [];
9974         }
9975         this.fireEvent("clear", this);
9976     },
9977
9978     /**
9979      * Inserts Records to the Store at the given index and fires the add event.
9980      * @param {Number} index The start index at which to insert the passed Records.
9981      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
9982      */
9983     insert : function(index, records){
9984         records = [].concat(records);
9985         for(var i = 0, len = records.length; i < len; i++){
9986             this.data.insert(index, records[i]);
9987             records[i].join(this);
9988         }
9989         this.fireEvent("add", this, records, index);
9990     },
9991
9992     /**
9993      * Get the index within the cache of the passed Record.
9994      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
9995      * @return {Number} The index of the passed Record. Returns -1 if not found.
9996      */
9997     indexOf : function(record){
9998         return this.data.indexOf(record);
9999     },
10000
10001     /**
10002      * Get the index within the cache of the Record with the passed id.
10003      * @param {String} id The id of the Record to find.
10004      * @return {Number} The index of the Record. Returns -1 if not found.
10005      */
10006     indexOfId : function(id){
10007         return this.data.indexOfKey(id);
10008     },
10009
10010     /**
10011      * Get the Record with the specified id.
10012      * @param {String} id The id of the Record to find.
10013      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10014      */
10015     getById : function(id){
10016         return this.data.key(id);
10017     },
10018
10019     /**
10020      * Get the Record at the specified index.
10021      * @param {Number} index The index of the Record to find.
10022      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10023      */
10024     getAt : function(index){
10025         return this.data.itemAt(index);
10026     },
10027
10028     /**
10029      * Returns a range of Records between specified indices.
10030      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10031      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10032      * @return {Roo.data.Record[]} An array of Records
10033      */
10034     getRange : function(start, end){
10035         return this.data.getRange(start, end);
10036     },
10037
10038     // private
10039     storeOptions : function(o){
10040         o = Roo.apply({}, o);
10041         delete o.callback;
10042         delete o.scope;
10043         this.lastOptions = o;
10044     },
10045
10046     /**
10047      * Loads the Record cache from the configured Proxy using the configured Reader.
10048      * <p>
10049      * If using remote paging, then the first load call must specify the <em>start</em>
10050      * and <em>limit</em> properties in the options.params property to establish the initial
10051      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10052      * <p>
10053      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10054      * and this call will return before the new data has been loaded. Perform any post-processing
10055      * in a callback function, or in a "load" event handler.</strong>
10056      * <p>
10057      * @param {Object} options An object containing properties which control loading options:<ul>
10058      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10059      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10060      * passed the following arguments:<ul>
10061      * <li>r : Roo.data.Record[]</li>
10062      * <li>options: Options object from the load call</li>
10063      * <li>success: Boolean success indicator</li></ul></li>
10064      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10065      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10066      * </ul>
10067      */
10068     load : function(options){
10069         options = options || {};
10070         if(this.fireEvent("beforeload", this, options) !== false){
10071             this.storeOptions(options);
10072             var p = Roo.apply(options.params || {}, this.baseParams);
10073             // if meta was not loaded from remote source.. try requesting it.
10074             if (!this.reader.metaFromRemote) {
10075                 p._requestMeta = 1;
10076             }
10077             if(this.sortInfo && this.remoteSort){
10078                 var pn = this.paramNames;
10079                 p[pn["sort"]] = this.sortInfo.field;
10080                 p[pn["dir"]] = this.sortInfo.direction;
10081             }
10082             if (this.multiSort) {
10083                 var pn = this.paramNames;
10084                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10085             }
10086             
10087             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10088         }
10089     },
10090
10091     /**
10092      * Reloads the Record cache from the configured Proxy using the configured Reader and
10093      * the options from the last load operation performed.
10094      * @param {Object} options (optional) An object containing properties which may override the options
10095      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10096      * the most recently used options are reused).
10097      */
10098     reload : function(options){
10099         this.load(Roo.applyIf(options||{}, this.lastOptions));
10100     },
10101
10102     // private
10103     // Called as a callback by the Reader during a load operation.
10104     loadRecords : function(o, options, success){
10105         if(!o || success === false){
10106             if(success !== false){
10107                 this.fireEvent("load", this, [], options, o);
10108             }
10109             if(options.callback){
10110                 options.callback.call(options.scope || this, [], options, false);
10111             }
10112             return;
10113         }
10114         // if data returned failure - throw an exception.
10115         if (o.success === false) {
10116             // show a message if no listener is registered.
10117             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10118                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10119             }
10120             // loadmask wil be hooked into this..
10121             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10122             return;
10123         }
10124         var r = o.records, t = o.totalRecords || r.length;
10125         
10126         this.fireEvent("beforeloadadd", this, r, options, o);
10127         
10128         if(!options || options.add !== true){
10129             if(this.pruneModifiedRecords){
10130                 this.modified = [];
10131             }
10132             for(var i = 0, len = r.length; i < len; i++){
10133                 r[i].join(this);
10134             }
10135             if(this.snapshot){
10136                 this.data = this.snapshot;
10137                 delete this.snapshot;
10138             }
10139             this.data.clear();
10140             this.data.addAll(r);
10141             this.totalLength = t;
10142             this.applySort();
10143             this.fireEvent("datachanged", this);
10144         }else{
10145             this.totalLength = Math.max(t, this.data.length+r.length);
10146             this.add(r);
10147         }
10148         this.fireEvent("load", this, r, options, o);
10149         if(options.callback){
10150             options.callback.call(options.scope || this, r, options, true);
10151         }
10152     },
10153
10154
10155     /**
10156      * Loads data from a passed data block. A Reader which understands the format of the data
10157      * must have been configured in the constructor.
10158      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10159      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10160      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10161      */
10162     loadData : function(o, append){
10163         var r = this.reader.readRecords(o);
10164         this.loadRecords(r, {add: append}, true);
10165     },
10166
10167     /**
10168      * Gets the number of cached records.
10169      * <p>
10170      * <em>If using paging, this may not be the total size of the dataset. If the data object
10171      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10172      * the data set size</em>
10173      */
10174     getCount : function(){
10175         return this.data.length || 0;
10176     },
10177
10178     /**
10179      * Gets the total number of records in the dataset as returned by the server.
10180      * <p>
10181      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10182      * the dataset size</em>
10183      */
10184     getTotalCount : function(){
10185         return this.totalLength || 0;
10186     },
10187
10188     /**
10189      * Returns the sort state of the Store as an object with two properties:
10190      * <pre><code>
10191  field {String} The name of the field by which the Records are sorted
10192  direction {String} The sort order, "ASC" or "DESC"
10193      * </code></pre>
10194      */
10195     getSortState : function(){
10196         return this.sortInfo;
10197     },
10198
10199     // private
10200     applySort : function(){
10201         if(this.sortInfo && !this.remoteSort){
10202             var s = this.sortInfo, f = s.field;
10203             var st = this.fields.get(f).sortType;
10204             var fn = function(r1, r2){
10205                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10206                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10207             };
10208             this.data.sort(s.direction, fn);
10209             if(this.snapshot && this.snapshot != this.data){
10210                 this.snapshot.sort(s.direction, fn);
10211             }
10212         }
10213     },
10214
10215     /**
10216      * Sets the default sort column and order to be used by the next load operation.
10217      * @param {String} fieldName The name of the field to sort by.
10218      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10219      */
10220     setDefaultSort : function(field, dir){
10221         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10222     },
10223
10224     /**
10225      * Sort the Records.
10226      * If remote sorting is used, the sort is performed on the server, and the cache is
10227      * reloaded. If local sorting is used, the cache is sorted internally.
10228      * @param {String} fieldName The name of the field to sort by.
10229      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10230      */
10231     sort : function(fieldName, dir){
10232         var f = this.fields.get(fieldName);
10233         if(!dir){
10234             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10235             
10236             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10237                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10238             }else{
10239                 dir = f.sortDir;
10240             }
10241         }
10242         this.sortToggle[f.name] = dir;
10243         this.sortInfo = {field: f.name, direction: dir};
10244         if(!this.remoteSort){
10245             this.applySort();
10246             this.fireEvent("datachanged", this);
10247         }else{
10248             this.load(this.lastOptions);
10249         }
10250     },
10251
10252     /**
10253      * Calls the specified function for each of the Records in the cache.
10254      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10255      * Returning <em>false</em> aborts and exits the iteration.
10256      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10257      */
10258     each : function(fn, scope){
10259         this.data.each(fn, scope);
10260     },
10261
10262     /**
10263      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10264      * (e.g., during paging).
10265      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10266      */
10267     getModifiedRecords : function(){
10268         return this.modified;
10269     },
10270
10271     // private
10272     createFilterFn : function(property, value, anyMatch){
10273         if(!value.exec){ // not a regex
10274             value = String(value);
10275             if(value.length == 0){
10276                 return false;
10277             }
10278             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10279         }
10280         return function(r){
10281             return value.test(r.data[property]);
10282         };
10283     },
10284
10285     /**
10286      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10287      * @param {String} property A field on your records
10288      * @param {Number} start The record index to start at (defaults to 0)
10289      * @param {Number} end The last record index to include (defaults to length - 1)
10290      * @return {Number} The sum
10291      */
10292     sum : function(property, start, end){
10293         var rs = this.data.items, v = 0;
10294         start = start || 0;
10295         end = (end || end === 0) ? end : rs.length-1;
10296
10297         for(var i = start; i <= end; i++){
10298             v += (rs[i].data[property] || 0);
10299         }
10300         return v;
10301     },
10302
10303     /**
10304      * Filter the records by a specified property.
10305      * @param {String} field A field on your records
10306      * @param {String/RegExp} value Either a string that the field
10307      * should start with or a RegExp to test against the field
10308      * @param {Boolean} anyMatch True to match any part not just the beginning
10309      */
10310     filter : function(property, value, anyMatch){
10311         var fn = this.createFilterFn(property, value, anyMatch);
10312         return fn ? this.filterBy(fn) : this.clearFilter();
10313     },
10314
10315     /**
10316      * Filter by a function. The specified function will be called with each
10317      * record in this data source. If the function returns true the record is included,
10318      * otherwise it is filtered.
10319      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10320      * @param {Object} scope (optional) The scope of the function (defaults to this)
10321      */
10322     filterBy : function(fn, scope){
10323         this.snapshot = this.snapshot || this.data;
10324         this.data = this.queryBy(fn, scope||this);
10325         this.fireEvent("datachanged", this);
10326     },
10327
10328     /**
10329      * Query the records by a specified property.
10330      * @param {String} field A field on your records
10331      * @param {String/RegExp} value Either a string that the field
10332      * should start with or a RegExp to test against the field
10333      * @param {Boolean} anyMatch True to match any part not just the beginning
10334      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10335      */
10336     query : function(property, value, anyMatch){
10337         var fn = this.createFilterFn(property, value, anyMatch);
10338         return fn ? this.queryBy(fn) : this.data.clone();
10339     },
10340
10341     /**
10342      * Query by a function. The specified function will be called with each
10343      * record in this data source. If the function returns true the record is included
10344      * in the results.
10345      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10346      * @param {Object} scope (optional) The scope of the function (defaults to this)
10347       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10348      **/
10349     queryBy : function(fn, scope){
10350         var data = this.snapshot || this.data;
10351         return data.filterBy(fn, scope||this);
10352     },
10353
10354     /**
10355      * Collects unique values for a particular dataIndex from this store.
10356      * @param {String} dataIndex The property to collect
10357      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10358      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10359      * @return {Array} An array of the unique values
10360      **/
10361     collect : function(dataIndex, allowNull, bypassFilter){
10362         var d = (bypassFilter === true && this.snapshot) ?
10363                 this.snapshot.items : this.data.items;
10364         var v, sv, r = [], l = {};
10365         for(var i = 0, len = d.length; i < len; i++){
10366             v = d[i].data[dataIndex];
10367             sv = String(v);
10368             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10369                 l[sv] = true;
10370                 r[r.length] = v;
10371             }
10372         }
10373         return r;
10374     },
10375
10376     /**
10377      * Revert to a view of the Record cache with no filtering applied.
10378      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10379      */
10380     clearFilter : function(suppressEvent){
10381         if(this.snapshot && this.snapshot != this.data){
10382             this.data = this.snapshot;
10383             delete this.snapshot;
10384             if(suppressEvent !== true){
10385                 this.fireEvent("datachanged", this);
10386             }
10387         }
10388     },
10389
10390     // private
10391     afterEdit : function(record){
10392         if(this.modified.indexOf(record) == -1){
10393             this.modified.push(record);
10394         }
10395         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10396     },
10397     
10398     // private
10399     afterReject : function(record){
10400         this.modified.remove(record);
10401         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10402     },
10403
10404     // private
10405     afterCommit : function(record){
10406         this.modified.remove(record);
10407         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10408     },
10409
10410     /**
10411      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10412      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10413      */
10414     commitChanges : function(){
10415         var m = this.modified.slice(0);
10416         this.modified = [];
10417         for(var i = 0, len = m.length; i < len; i++){
10418             m[i].commit();
10419         }
10420     },
10421
10422     /**
10423      * Cancel outstanding changes on all changed records.
10424      */
10425     rejectChanges : function(){
10426         var m = this.modified.slice(0);
10427         this.modified = [];
10428         for(var i = 0, len = m.length; i < len; i++){
10429             m[i].reject();
10430         }
10431     },
10432
10433     onMetaChange : function(meta, rtype, o){
10434         this.recordType = rtype;
10435         this.fields = rtype.prototype.fields;
10436         delete this.snapshot;
10437         this.sortInfo = meta.sortInfo || this.sortInfo;
10438         this.modified = [];
10439         this.fireEvent('metachange', this, this.reader.meta);
10440     },
10441     
10442     moveIndex : function(data, type)
10443     {
10444         var index = this.indexOf(data);
10445         
10446         var newIndex = index + type;
10447         
10448         this.remove(data);
10449         
10450         this.insert(newIndex, data);
10451         
10452     }
10453 });/*
10454  * Based on:
10455  * Ext JS Library 1.1.1
10456  * Copyright(c) 2006-2007, Ext JS, LLC.
10457  *
10458  * Originally Released Under LGPL - original licence link has changed is not relivant.
10459  *
10460  * Fork - LGPL
10461  * <script type="text/javascript">
10462  */
10463
10464 /**
10465  * @class Roo.data.SimpleStore
10466  * @extends Roo.data.Store
10467  * Small helper class to make creating Stores from Array data easier.
10468  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10469  * @cfg {Array} fields An array of field definition objects, or field name strings.
10470  * @cfg {Array} data The multi-dimensional array of data
10471  * @constructor
10472  * @param {Object} config
10473  */
10474 Roo.data.SimpleStore = function(config){
10475     Roo.data.SimpleStore.superclass.constructor.call(this, {
10476         isLocal : true,
10477         reader: new Roo.data.ArrayReader({
10478                 id: config.id
10479             },
10480             Roo.data.Record.create(config.fields)
10481         ),
10482         proxy : new Roo.data.MemoryProxy(config.data)
10483     });
10484     this.load();
10485 };
10486 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10487  * Based on:
10488  * Ext JS Library 1.1.1
10489  * Copyright(c) 2006-2007, Ext JS, LLC.
10490  *
10491  * Originally Released Under LGPL - original licence link has changed is not relivant.
10492  *
10493  * Fork - LGPL
10494  * <script type="text/javascript">
10495  */
10496
10497 /**
10498 /**
10499  * @extends Roo.data.Store
10500  * @class Roo.data.JsonStore
10501  * Small helper class to make creating Stores for JSON data easier. <br/>
10502 <pre><code>
10503 var store = new Roo.data.JsonStore({
10504     url: 'get-images.php',
10505     root: 'images',
10506     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10507 });
10508 </code></pre>
10509  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10510  * JsonReader and HttpProxy (unless inline data is provided).</b>
10511  * @cfg {Array} fields An array of field definition objects, or field name strings.
10512  * @constructor
10513  * @param {Object} config
10514  */
10515 Roo.data.JsonStore = function(c){
10516     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10517         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10518         reader: new Roo.data.JsonReader(c, c.fields)
10519     }));
10520 };
10521 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10522  * Based on:
10523  * Ext JS Library 1.1.1
10524  * Copyright(c) 2006-2007, Ext JS, LLC.
10525  *
10526  * Originally Released Under LGPL - original licence link has changed is not relivant.
10527  *
10528  * Fork - LGPL
10529  * <script type="text/javascript">
10530  */
10531
10532  
10533 Roo.data.Field = function(config){
10534     if(typeof config == "string"){
10535         config = {name: config};
10536     }
10537     Roo.apply(this, config);
10538     
10539     if(!this.type){
10540         this.type = "auto";
10541     }
10542     
10543     var st = Roo.data.SortTypes;
10544     // named sortTypes are supported, here we look them up
10545     if(typeof this.sortType == "string"){
10546         this.sortType = st[this.sortType];
10547     }
10548     
10549     // set default sortType for strings and dates
10550     if(!this.sortType){
10551         switch(this.type){
10552             case "string":
10553                 this.sortType = st.asUCString;
10554                 break;
10555             case "date":
10556                 this.sortType = st.asDate;
10557                 break;
10558             default:
10559                 this.sortType = st.none;
10560         }
10561     }
10562
10563     // define once
10564     var stripRe = /[\$,%]/g;
10565
10566     // prebuilt conversion function for this field, instead of
10567     // switching every time we're reading a value
10568     if(!this.convert){
10569         var cv, dateFormat = this.dateFormat;
10570         switch(this.type){
10571             case "":
10572             case "auto":
10573             case undefined:
10574                 cv = function(v){ return v; };
10575                 break;
10576             case "string":
10577                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10578                 break;
10579             case "int":
10580                 cv = function(v){
10581                     return v !== undefined && v !== null && v !== '' ?
10582                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10583                     };
10584                 break;
10585             case "float":
10586                 cv = function(v){
10587                     return v !== undefined && v !== null && v !== '' ?
10588                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10589                     };
10590                 break;
10591             case "bool":
10592             case "boolean":
10593                 cv = function(v){ return v === true || v === "true" || v == 1; };
10594                 break;
10595             case "date":
10596                 cv = function(v){
10597                     if(!v){
10598                         return '';
10599                     }
10600                     if(v instanceof Date){
10601                         return v;
10602                     }
10603                     if(dateFormat){
10604                         if(dateFormat == "timestamp"){
10605                             return new Date(v*1000);
10606                         }
10607                         return Date.parseDate(v, dateFormat);
10608                     }
10609                     var parsed = Date.parse(v);
10610                     return parsed ? new Date(parsed) : null;
10611                 };
10612              break;
10613             
10614         }
10615         this.convert = cv;
10616     }
10617 };
10618
10619 Roo.data.Field.prototype = {
10620     dateFormat: null,
10621     defaultValue: "",
10622     mapping: null,
10623     sortType : null,
10624     sortDir : "ASC"
10625 };/*
10626  * Based on:
10627  * Ext JS Library 1.1.1
10628  * Copyright(c) 2006-2007, Ext JS, LLC.
10629  *
10630  * Originally Released Under LGPL - original licence link has changed is not relivant.
10631  *
10632  * Fork - LGPL
10633  * <script type="text/javascript">
10634  */
10635  
10636 // Base class for reading structured data from a data source.  This class is intended to be
10637 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10638
10639 /**
10640  * @class Roo.data.DataReader
10641  * Base class for reading structured data from a data source.  This class is intended to be
10642  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10643  */
10644
10645 Roo.data.DataReader = function(meta, recordType){
10646     
10647     this.meta = meta;
10648     
10649     this.recordType = recordType instanceof Array ? 
10650         Roo.data.Record.create(recordType) : recordType;
10651 };
10652
10653 Roo.data.DataReader.prototype = {
10654      /**
10655      * Create an empty record
10656      * @param {Object} data (optional) - overlay some values
10657      * @return {Roo.data.Record} record created.
10658      */
10659     newRow :  function(d) {
10660         var da =  {};
10661         this.recordType.prototype.fields.each(function(c) {
10662             switch( c.type) {
10663                 case 'int' : da[c.name] = 0; break;
10664                 case 'date' : da[c.name] = new Date(); break;
10665                 case 'float' : da[c.name] = 0.0; break;
10666                 case 'boolean' : da[c.name] = false; break;
10667                 default : da[c.name] = ""; break;
10668             }
10669             
10670         });
10671         return new this.recordType(Roo.apply(da, d));
10672     }
10673     
10674 };/*
10675  * Based on:
10676  * Ext JS Library 1.1.1
10677  * Copyright(c) 2006-2007, Ext JS, LLC.
10678  *
10679  * Originally Released Under LGPL - original licence link has changed is not relivant.
10680  *
10681  * Fork - LGPL
10682  * <script type="text/javascript">
10683  */
10684
10685 /**
10686  * @class Roo.data.DataProxy
10687  * @extends Roo.data.Observable
10688  * This class is an abstract base class for implementations which provide retrieval of
10689  * unformatted data objects.<br>
10690  * <p>
10691  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10692  * (of the appropriate type which knows how to parse the data object) to provide a block of
10693  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10694  * <p>
10695  * Custom implementations must implement the load method as described in
10696  * {@link Roo.data.HttpProxy#load}.
10697  */
10698 Roo.data.DataProxy = function(){
10699     this.addEvents({
10700         /**
10701          * @event beforeload
10702          * Fires before a network request is made to retrieve a data object.
10703          * @param {Object} This DataProxy object.
10704          * @param {Object} params The params parameter to the load function.
10705          */
10706         beforeload : true,
10707         /**
10708          * @event load
10709          * Fires before the load method's callback is called.
10710          * @param {Object} This DataProxy object.
10711          * @param {Object} o The data object.
10712          * @param {Object} arg The callback argument object passed to the load function.
10713          */
10714         load : true,
10715         /**
10716          * @event loadexception
10717          * Fires if an Exception occurs during data retrieval.
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          * @param {Object} e The Exception.
10722          */
10723         loadexception : true
10724     });
10725     Roo.data.DataProxy.superclass.constructor.call(this);
10726 };
10727
10728 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10729
10730     /**
10731      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10732      */
10733 /*
10734  * Based on:
10735  * Ext JS Library 1.1.1
10736  * Copyright(c) 2006-2007, Ext JS, LLC.
10737  *
10738  * Originally Released Under LGPL - original licence link has changed is not relivant.
10739  *
10740  * Fork - LGPL
10741  * <script type="text/javascript">
10742  */
10743 /**
10744  * @class Roo.data.MemoryProxy
10745  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10746  * to the Reader when its load method is called.
10747  * @constructor
10748  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10749  */
10750 Roo.data.MemoryProxy = function(data){
10751     if (data.data) {
10752         data = data.data;
10753     }
10754     Roo.data.MemoryProxy.superclass.constructor.call(this);
10755     this.data = data;
10756 };
10757
10758 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10759     /**
10760      * Load data from the requested source (in this case an in-memory
10761      * data object passed to the constructor), read the data object into
10762      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10763      * process that block using the passed callback.
10764      * @param {Object} params This parameter is not used by the MemoryProxy class.
10765      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10766      * object into a block of Roo.data.Records.
10767      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10768      * The function must be passed <ul>
10769      * <li>The Record block object</li>
10770      * <li>The "arg" argument from the load function</li>
10771      * <li>A boolean success indicator</li>
10772      * </ul>
10773      * @param {Object} scope The scope in which to call the callback
10774      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10775      */
10776     load : function(params, reader, callback, scope, arg){
10777         params = params || {};
10778         var result;
10779         try {
10780             result = reader.readRecords(this.data);
10781         }catch(e){
10782             this.fireEvent("loadexception", this, arg, null, e);
10783             callback.call(scope, null, arg, false);
10784             return;
10785         }
10786         callback.call(scope, result, arg, true);
10787     },
10788     
10789     // private
10790     update : function(params, records){
10791         
10792     }
10793 });/*
10794  * Based on:
10795  * Ext JS Library 1.1.1
10796  * Copyright(c) 2006-2007, Ext JS, LLC.
10797  *
10798  * Originally Released Under LGPL - original licence link has changed is not relivant.
10799  *
10800  * Fork - LGPL
10801  * <script type="text/javascript">
10802  */
10803 /**
10804  * @class Roo.data.HttpProxy
10805  * @extends Roo.data.DataProxy
10806  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10807  * configured to reference a certain URL.<br><br>
10808  * <p>
10809  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10810  * from which the running page was served.<br><br>
10811  * <p>
10812  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10813  * <p>
10814  * Be aware that to enable the browser to parse an XML document, the server must set
10815  * the Content-Type header in the HTTP response to "text/xml".
10816  * @constructor
10817  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10818  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10819  * will be used to make the request.
10820  */
10821 Roo.data.HttpProxy = function(conn){
10822     Roo.data.HttpProxy.superclass.constructor.call(this);
10823     // is conn a conn config or a real conn?
10824     this.conn = conn;
10825     this.useAjax = !conn || !conn.events;
10826   
10827 };
10828
10829 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10830     // thse are take from connection...
10831     
10832     /**
10833      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10834      */
10835     /**
10836      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10837      * extra parameters to each request made by this object. (defaults to undefined)
10838      */
10839     /**
10840      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10841      *  to each request made by this object. (defaults to undefined)
10842      */
10843     /**
10844      * @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)
10845      */
10846     /**
10847      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10848      */
10849      /**
10850      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10851      * @type Boolean
10852      */
10853   
10854
10855     /**
10856      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10857      * @type Boolean
10858      */
10859     /**
10860      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10861      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10862      * a finer-grained basis than the DataProxy events.
10863      */
10864     getConnection : function(){
10865         return this.useAjax ? Roo.Ajax : this.conn;
10866     },
10867
10868     /**
10869      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10870      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10871      * process that block using the passed callback.
10872      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10873      * for the request to the remote server.
10874      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10875      * object into a block of Roo.data.Records.
10876      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10877      * The function must be passed <ul>
10878      * <li>The Record block object</li>
10879      * <li>The "arg" argument from the load function</li>
10880      * <li>A boolean success indicator</li>
10881      * </ul>
10882      * @param {Object} scope The scope in which to call the callback
10883      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10884      */
10885     load : function(params, reader, callback, scope, arg){
10886         if(this.fireEvent("beforeload", this, params) !== false){
10887             var  o = {
10888                 params : params || {},
10889                 request: {
10890                     callback : callback,
10891                     scope : scope,
10892                     arg : arg
10893                 },
10894                 reader: reader,
10895                 callback : this.loadResponse,
10896                 scope: this
10897             };
10898             if(this.useAjax){
10899                 Roo.applyIf(o, this.conn);
10900                 if(this.activeRequest){
10901                     Roo.Ajax.abort(this.activeRequest);
10902                 }
10903                 this.activeRequest = Roo.Ajax.request(o);
10904             }else{
10905                 this.conn.request(o);
10906             }
10907         }else{
10908             callback.call(scope||this, null, arg, false);
10909         }
10910     },
10911
10912     // private
10913     loadResponse : function(o, success, response){
10914         delete this.activeRequest;
10915         if(!success){
10916             this.fireEvent("loadexception", this, o, response);
10917             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10918             return;
10919         }
10920         var result;
10921         try {
10922             result = o.reader.read(response);
10923         }catch(e){
10924             this.fireEvent("loadexception", this, o, response, e);
10925             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10926             return;
10927         }
10928         
10929         this.fireEvent("load", this, o, o.request.arg);
10930         o.request.callback.call(o.request.scope, result, o.request.arg, true);
10931     },
10932
10933     // private
10934     update : function(dataSet){
10935
10936     },
10937
10938     // private
10939     updateResponse : function(dataSet){
10940
10941     }
10942 });/*
10943  * Based on:
10944  * Ext JS Library 1.1.1
10945  * Copyright(c) 2006-2007, Ext JS, LLC.
10946  *
10947  * Originally Released Under LGPL - original licence link has changed is not relivant.
10948  *
10949  * Fork - LGPL
10950  * <script type="text/javascript">
10951  */
10952
10953 /**
10954  * @class Roo.data.ScriptTagProxy
10955  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
10956  * other than the originating domain of the running page.<br><br>
10957  * <p>
10958  * <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
10959  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
10960  * <p>
10961  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
10962  * source code that is used as the source inside a &lt;script> tag.<br><br>
10963  * <p>
10964  * In order for the browser to process the returned data, the server must wrap the data object
10965  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
10966  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
10967  * depending on whether the callback name was passed:
10968  * <p>
10969  * <pre><code>
10970 boolean scriptTag = false;
10971 String cb = request.getParameter("callback");
10972 if (cb != null) {
10973     scriptTag = true;
10974     response.setContentType("text/javascript");
10975 } else {
10976     response.setContentType("application/x-json");
10977 }
10978 Writer out = response.getWriter();
10979 if (scriptTag) {
10980     out.write(cb + "(");
10981 }
10982 out.print(dataBlock.toJsonString());
10983 if (scriptTag) {
10984     out.write(");");
10985 }
10986 </pre></code>
10987  *
10988  * @constructor
10989  * @param {Object} config A configuration object.
10990  */
10991 Roo.data.ScriptTagProxy = function(config){
10992     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
10993     Roo.apply(this, config);
10994     this.head = document.getElementsByTagName("head")[0];
10995 };
10996
10997 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
10998
10999 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11000     /**
11001      * @cfg {String} url The URL from which to request the data object.
11002      */
11003     /**
11004      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11005      */
11006     timeout : 30000,
11007     /**
11008      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11009      * the server the name of the callback function set up by the load call to process the returned data object.
11010      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11011      * javascript output which calls this named function passing the data object as its only parameter.
11012      */
11013     callbackParam : "callback",
11014     /**
11015      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11016      * name to the request.
11017      */
11018     nocache : true,
11019
11020     /**
11021      * Load data from the configured URL, read the data object into
11022      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11023      * process that block using the passed callback.
11024      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11025      * for the request to the remote server.
11026      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11027      * object into a block of Roo.data.Records.
11028      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11029      * The function must be passed <ul>
11030      * <li>The Record block object</li>
11031      * <li>The "arg" argument from the load function</li>
11032      * <li>A boolean success indicator</li>
11033      * </ul>
11034      * @param {Object} scope The scope in which to call the callback
11035      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11036      */
11037     load : function(params, reader, callback, scope, arg){
11038         if(this.fireEvent("beforeload", this, params) !== false){
11039
11040             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11041
11042             var url = this.url;
11043             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11044             if(this.nocache){
11045                 url += "&_dc=" + (new Date().getTime());
11046             }
11047             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11048             var trans = {
11049                 id : transId,
11050                 cb : "stcCallback"+transId,
11051                 scriptId : "stcScript"+transId,
11052                 params : params,
11053                 arg : arg,
11054                 url : url,
11055                 callback : callback,
11056                 scope : scope,
11057                 reader : reader
11058             };
11059             var conn = this;
11060
11061             window[trans.cb] = function(o){
11062                 conn.handleResponse(o, trans);
11063             };
11064
11065             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11066
11067             if(this.autoAbort !== false){
11068                 this.abort();
11069             }
11070
11071             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11072
11073             var script = document.createElement("script");
11074             script.setAttribute("src", url);
11075             script.setAttribute("type", "text/javascript");
11076             script.setAttribute("id", trans.scriptId);
11077             this.head.appendChild(script);
11078
11079             this.trans = trans;
11080         }else{
11081             callback.call(scope||this, null, arg, false);
11082         }
11083     },
11084
11085     // private
11086     isLoading : function(){
11087         return this.trans ? true : false;
11088     },
11089
11090     /**
11091      * Abort the current server request.
11092      */
11093     abort : function(){
11094         if(this.isLoading()){
11095             this.destroyTrans(this.trans);
11096         }
11097     },
11098
11099     // private
11100     destroyTrans : function(trans, isLoaded){
11101         this.head.removeChild(document.getElementById(trans.scriptId));
11102         clearTimeout(trans.timeoutId);
11103         if(isLoaded){
11104             window[trans.cb] = undefined;
11105             try{
11106                 delete window[trans.cb];
11107             }catch(e){}
11108         }else{
11109             // if hasn't been loaded, wait for load to remove it to prevent script error
11110             window[trans.cb] = function(){
11111                 window[trans.cb] = undefined;
11112                 try{
11113                     delete window[trans.cb];
11114                 }catch(e){}
11115             };
11116         }
11117     },
11118
11119     // private
11120     handleResponse : function(o, trans){
11121         this.trans = false;
11122         this.destroyTrans(trans, true);
11123         var result;
11124         try {
11125             result = trans.reader.readRecords(o);
11126         }catch(e){
11127             this.fireEvent("loadexception", this, o, trans.arg, e);
11128             trans.callback.call(trans.scope||window, null, trans.arg, false);
11129             return;
11130         }
11131         this.fireEvent("load", this, o, trans.arg);
11132         trans.callback.call(trans.scope||window, result, trans.arg, true);
11133     },
11134
11135     // private
11136     handleFailure : function(trans){
11137         this.trans = false;
11138         this.destroyTrans(trans, false);
11139         this.fireEvent("loadexception", this, null, trans.arg);
11140         trans.callback.call(trans.scope||window, null, trans.arg, false);
11141     }
11142 });/*
11143  * Based on:
11144  * Ext JS Library 1.1.1
11145  * Copyright(c) 2006-2007, Ext JS, LLC.
11146  *
11147  * Originally Released Under LGPL - original licence link has changed is not relivant.
11148  *
11149  * Fork - LGPL
11150  * <script type="text/javascript">
11151  */
11152
11153 /**
11154  * @class Roo.data.JsonReader
11155  * @extends Roo.data.DataReader
11156  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11157  * based on mappings in a provided Roo.data.Record constructor.
11158  * 
11159  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11160  * in the reply previously. 
11161  * 
11162  * <p>
11163  * Example code:
11164  * <pre><code>
11165 var RecordDef = Roo.data.Record.create([
11166     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11167     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11168 ]);
11169 var myReader = new Roo.data.JsonReader({
11170     totalProperty: "results",    // The property which contains the total dataset size (optional)
11171     root: "rows",                // The property which contains an Array of row objects
11172     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11173 }, RecordDef);
11174 </code></pre>
11175  * <p>
11176  * This would consume a JSON file like this:
11177  * <pre><code>
11178 { 'results': 2, 'rows': [
11179     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11180     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11181 }
11182 </code></pre>
11183  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11184  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11185  * paged from the remote server.
11186  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11187  * @cfg {String} root name of the property which contains the Array of row objects.
11188  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11189  * @cfg {Array} fields Array of field definition objects
11190  * @constructor
11191  * Create a new JsonReader
11192  * @param {Object} meta Metadata configuration options
11193  * @param {Object} recordType Either an Array of field definition objects,
11194  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11195  */
11196 Roo.data.JsonReader = function(meta, recordType){
11197     
11198     meta = meta || {};
11199     // set some defaults:
11200     Roo.applyIf(meta, {
11201         totalProperty: 'total',
11202         successProperty : 'success',
11203         root : 'data',
11204         id : 'id'
11205     });
11206     
11207     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11208 };
11209 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11210     
11211     /**
11212      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11213      * Used by Store query builder to append _requestMeta to params.
11214      * 
11215      */
11216     metaFromRemote : false,
11217     /**
11218      * This method is only used by a DataProxy which has retrieved data from a remote server.
11219      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11220      * @return {Object} data A data block which is used by an Roo.data.Store object as
11221      * a cache of Roo.data.Records.
11222      */
11223     read : function(response){
11224         var json = response.responseText;
11225        
11226         var o = /* eval:var:o */ eval("("+json+")");
11227         if(!o) {
11228             throw {message: "JsonReader.read: Json object not found"};
11229         }
11230         
11231         if(o.metaData){
11232             
11233             delete this.ef;
11234             this.metaFromRemote = true;
11235             this.meta = o.metaData;
11236             this.recordType = Roo.data.Record.create(o.metaData.fields);
11237             this.onMetaChange(this.meta, this.recordType, o);
11238         }
11239         return this.readRecords(o);
11240     },
11241
11242     // private function a store will implement
11243     onMetaChange : function(meta, recordType, o){
11244
11245     },
11246
11247     /**
11248          * @ignore
11249          */
11250     simpleAccess: function(obj, subsc) {
11251         return obj[subsc];
11252     },
11253
11254         /**
11255          * @ignore
11256          */
11257     getJsonAccessor: function(){
11258         var re = /[\[\.]/;
11259         return function(expr) {
11260             try {
11261                 return(re.test(expr))
11262                     ? new Function("obj", "return obj." + expr)
11263                     : function(obj){
11264                         return obj[expr];
11265                     };
11266             } catch(e){}
11267             return Roo.emptyFn;
11268         };
11269     }(),
11270
11271     /**
11272      * Create a data block containing Roo.data.Records from an XML document.
11273      * @param {Object} o An object which contains an Array of row objects in the property specified
11274      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11275      * which contains the total size of the dataset.
11276      * @return {Object} data A data block which is used by an Roo.data.Store object as
11277      * a cache of Roo.data.Records.
11278      */
11279     readRecords : function(o){
11280         /**
11281          * After any data loads, the raw JSON data is available for further custom processing.
11282          * @type Object
11283          */
11284         this.o = o;
11285         var s = this.meta, Record = this.recordType,
11286             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11287
11288 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11289         if (!this.ef) {
11290             if(s.totalProperty) {
11291                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11292                 }
11293                 if(s.successProperty) {
11294                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11295                 }
11296                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11297                 if (s.id) {
11298                         var g = this.getJsonAccessor(s.id);
11299                         this.getId = function(rec) {
11300                                 var r = g(rec);  
11301                                 return (r === undefined || r === "") ? null : r;
11302                         };
11303                 } else {
11304                         this.getId = function(){return null;};
11305                 }
11306             this.ef = [];
11307             for(var jj = 0; jj < fl; jj++){
11308                 f = fi[jj];
11309                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11310                 this.ef[jj] = this.getJsonAccessor(map);
11311             }
11312         }
11313
11314         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11315         if(s.totalProperty){
11316             var vt = parseInt(this.getTotal(o), 10);
11317             if(!isNaN(vt)){
11318                 totalRecords = vt;
11319             }
11320         }
11321         if(s.successProperty){
11322             var vs = this.getSuccess(o);
11323             if(vs === false || vs === 'false'){
11324                 success = false;
11325             }
11326         }
11327         var records = [];
11328         for(var i = 0; i < c; i++){
11329                 var n = root[i];
11330             var values = {};
11331             var id = this.getId(n);
11332             for(var j = 0; j < fl; j++){
11333                 f = fi[j];
11334             var v = this.ef[j](n);
11335             if (!f.convert) {
11336                 Roo.log('missing convert for ' + f.name);
11337                 Roo.log(f);
11338                 continue;
11339             }
11340             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11341             }
11342             var record = new Record(values, id);
11343             record.json = n;
11344             records[i] = record;
11345         }
11346         return {
11347             raw : o,
11348             success : success,
11349             records : records,
11350             totalRecords : totalRecords
11351         };
11352     }
11353 });/*
11354  * Based on:
11355  * Ext JS Library 1.1.1
11356  * Copyright(c) 2006-2007, Ext JS, LLC.
11357  *
11358  * Originally Released Under LGPL - original licence link has changed is not relivant.
11359  *
11360  * Fork - LGPL
11361  * <script type="text/javascript">
11362  */
11363
11364 /**
11365  * @class Roo.data.ArrayReader
11366  * @extends Roo.data.DataReader
11367  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11368  * Each element of that Array represents a row of data fields. The
11369  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11370  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11371  * <p>
11372  * Example code:.
11373  * <pre><code>
11374 var RecordDef = Roo.data.Record.create([
11375     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11376     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11377 ]);
11378 var myReader = new Roo.data.ArrayReader({
11379     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11380 }, RecordDef);
11381 </code></pre>
11382  * <p>
11383  * This would consume an Array like this:
11384  * <pre><code>
11385 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11386   </code></pre>
11387  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11388  * @constructor
11389  * Create a new JsonReader
11390  * @param {Object} meta Metadata configuration options.
11391  * @param {Object} recordType Either an Array of field definition objects
11392  * as specified to {@link Roo.data.Record#create},
11393  * or an {@link Roo.data.Record} object
11394  * created using {@link Roo.data.Record#create}.
11395  */
11396 Roo.data.ArrayReader = function(meta, recordType){
11397     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11398 };
11399
11400 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11401     /**
11402      * Create a data block containing Roo.data.Records from an XML document.
11403      * @param {Object} o An Array of row objects which represents the dataset.
11404      * @return {Object} data A data block which is used by an Roo.data.Store object as
11405      * a cache of Roo.data.Records.
11406      */
11407     readRecords : function(o){
11408         var sid = this.meta ? this.meta.id : null;
11409         var recordType = this.recordType, fields = recordType.prototype.fields;
11410         var records = [];
11411         var root = o;
11412             for(var i = 0; i < root.length; i++){
11413                     var n = root[i];
11414                 var values = {};
11415                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11416                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11417                 var f = fields.items[j];
11418                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11419                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11420                 v = f.convert(v);
11421                 values[f.name] = v;
11422             }
11423                 var record = new recordType(values, id);
11424                 record.json = n;
11425                 records[records.length] = record;
11426             }
11427             return {
11428                 records : records,
11429                 totalRecords : records.length
11430             };
11431     }
11432 });/*
11433  * - LGPL
11434  * * 
11435  */
11436
11437 /**
11438  * @class Roo.bootstrap.ComboBox
11439  * @extends Roo.bootstrap.TriggerField
11440  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11441  * @cfg {Boolean} append (true|false) default false
11442  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11443  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11444  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11445  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11446  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11447  * @cfg {Boolean} animate default true
11448  * @cfg {Boolean} emptyResultText only for touch device
11449  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11450  * @constructor
11451  * Create a new ComboBox.
11452  * @param {Object} config Configuration options
11453  */
11454 Roo.bootstrap.ComboBox = function(config){
11455     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11456     this.addEvents({
11457         /**
11458          * @event expand
11459          * Fires when the dropdown list is expanded
11460              * @param {Roo.bootstrap.ComboBox} combo This combo box
11461              */
11462         'expand' : true,
11463         /**
11464          * @event collapse
11465          * Fires when the dropdown list is collapsed
11466              * @param {Roo.bootstrap.ComboBox} combo This combo box
11467              */
11468         'collapse' : true,
11469         /**
11470          * @event beforeselect
11471          * Fires before a list item is selected. Return false to cancel the selection.
11472              * @param {Roo.bootstrap.ComboBox} combo This combo box
11473              * @param {Roo.data.Record} record The data record returned from the underlying store
11474              * @param {Number} index The index of the selected item in the dropdown list
11475              */
11476         'beforeselect' : true,
11477         /**
11478          * @event select
11479          * Fires when a list item is selected
11480              * @param {Roo.bootstrap.ComboBox} combo This combo box
11481              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11482              * @param {Number} index The index of the selected item in the dropdown list
11483              */
11484         'select' : true,
11485         /**
11486          * @event beforequery
11487          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11488          * The event object passed has these properties:
11489              * @param {Roo.bootstrap.ComboBox} combo This combo box
11490              * @param {String} query The query
11491              * @param {Boolean} forceAll true to force "all" query
11492              * @param {Boolean} cancel true to cancel the query
11493              * @param {Object} e The query event object
11494              */
11495         'beforequery': true,
11496          /**
11497          * @event add
11498          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11499              * @param {Roo.bootstrap.ComboBox} combo This combo box
11500              */
11501         'add' : true,
11502         /**
11503          * @event edit
11504          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11505              * @param {Roo.bootstrap.ComboBox} combo This combo box
11506              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11507              */
11508         'edit' : true,
11509         /**
11510          * @event remove
11511          * Fires when the remove value from the combobox array
11512              * @param {Roo.bootstrap.ComboBox} combo This combo box
11513              */
11514         'remove' : true,
11515         /**
11516          * @event specialfilter
11517          * Fires when specialfilter
11518             * @param {Roo.bootstrap.ComboBox} combo This combo box
11519             */
11520         'specialfilter' : true,
11521         /**
11522          * @event tick
11523          * Fires when tick the element
11524             * @param {Roo.bootstrap.ComboBox} combo This combo box
11525             */
11526         'tick' : true,
11527         /**
11528          * @event touchviewdisplay
11529          * Fires when touch view require special display (default is using displayField)
11530             * @param {Roo.bootstrap.ComboBox} combo This combo box
11531             * @param {Object} cfg set html .
11532             */
11533         'touchviewdisplay' : true
11534         
11535     });
11536     
11537     this.item = [];
11538     this.tickItems = [];
11539     
11540     this.selectedIndex = -1;
11541     if(this.mode == 'local'){
11542         if(config.queryDelay === undefined){
11543             this.queryDelay = 10;
11544         }
11545         if(config.minChars === undefined){
11546             this.minChars = 0;
11547         }
11548     }
11549 };
11550
11551 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11552      
11553     /**
11554      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11555      * rendering into an Roo.Editor, defaults to false)
11556      */
11557     /**
11558      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11559      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11560      */
11561     /**
11562      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11563      */
11564     /**
11565      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11566      * the dropdown list (defaults to undefined, with no header element)
11567      */
11568
11569      /**
11570      * @cfg {String/Roo.Template} tpl The template to use to render the output
11571      */
11572      
11573      /**
11574      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11575      */
11576     listWidth: undefined,
11577     /**
11578      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11579      * mode = 'remote' or 'text' if mode = 'local')
11580      */
11581     displayField: undefined,
11582     
11583     /**
11584      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11585      * mode = 'remote' or 'value' if mode = 'local'). 
11586      * Note: use of a valueField requires the user make a selection
11587      * in order for a value to be mapped.
11588      */
11589     valueField: undefined,
11590     
11591     
11592     /**
11593      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11594      * field's data value (defaults to the underlying DOM element's name)
11595      */
11596     hiddenName: undefined,
11597     /**
11598      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11599      */
11600     listClass: '',
11601     /**
11602      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11603      */
11604     selectedClass: 'active',
11605     
11606     /**
11607      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11608      */
11609     shadow:'sides',
11610     /**
11611      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11612      * anchor positions (defaults to 'tl-bl')
11613      */
11614     listAlign: 'tl-bl?',
11615     /**
11616      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11617      */
11618     maxHeight: 300,
11619     /**
11620      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11621      * query specified by the allQuery config option (defaults to 'query')
11622      */
11623     triggerAction: 'query',
11624     /**
11625      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11626      * (defaults to 4, does not apply if editable = false)
11627      */
11628     minChars : 4,
11629     /**
11630      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11631      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11632      */
11633     typeAhead: false,
11634     /**
11635      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11636      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11637      */
11638     queryDelay: 500,
11639     /**
11640      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11641      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11642      */
11643     pageSize: 0,
11644     /**
11645      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11646      * when editable = true (defaults to false)
11647      */
11648     selectOnFocus:false,
11649     /**
11650      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11651      */
11652     queryParam: 'query',
11653     /**
11654      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11655      * when mode = 'remote' (defaults to 'Loading...')
11656      */
11657     loadingText: 'Loading...',
11658     /**
11659      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11660      */
11661     resizable: false,
11662     /**
11663      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11664      */
11665     handleHeight : 8,
11666     /**
11667      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11668      * traditional select (defaults to true)
11669      */
11670     editable: true,
11671     /**
11672      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11673      */
11674     allQuery: '',
11675     /**
11676      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11677      */
11678     mode: 'remote',
11679     /**
11680      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11681      * listWidth has a higher value)
11682      */
11683     minListWidth : 70,
11684     /**
11685      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11686      * allow the user to set arbitrary text into the field (defaults to false)
11687      */
11688     forceSelection:false,
11689     /**
11690      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11691      * if typeAhead = true (defaults to 250)
11692      */
11693     typeAheadDelay : 250,
11694     /**
11695      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11696      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11697      */
11698     valueNotFoundText : undefined,
11699     /**
11700      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11701      */
11702     blockFocus : false,
11703     
11704     /**
11705      * @cfg {Boolean} disableClear Disable showing of clear button.
11706      */
11707     disableClear : false,
11708     /**
11709      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11710      */
11711     alwaysQuery : false,
11712     
11713     /**
11714      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11715      */
11716     multiple : false,
11717     
11718     /**
11719      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11720      */
11721     invalidClass : "has-warning",
11722     
11723     /**
11724      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11725      */
11726     validClass : "has-success",
11727     
11728     /**
11729      * @cfg {Boolean} specialFilter (true|false) special filter default false
11730      */
11731     specialFilter : false,
11732     
11733     /**
11734      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11735      */
11736     mobileTouchView : true,
11737     
11738     //private
11739     addicon : false,
11740     editicon: false,
11741     
11742     page: 0,
11743     hasQuery: false,
11744     append: false,
11745     loadNext: false,
11746     autoFocus : true,
11747     tickable : false,
11748     btnPosition : 'right',
11749     triggerList : true,
11750     showToggleBtn : true,
11751     animate : true,
11752     emptyResultText: 'Empty',
11753     triggerText : 'Select',
11754     
11755     // element that contains real text value.. (when hidden is used..)
11756     
11757     getAutoCreate : function()
11758     {
11759         var cfg = false;
11760         
11761         /*
11762          * Touch Devices
11763          */
11764         
11765         if(Roo.isTouch && this.mobileTouchView){
11766             cfg = this.getAutoCreateTouchView();
11767             return cfg;;
11768         }
11769         
11770         /*
11771          *  Normal ComboBox
11772          */
11773         if(!this.tickable){
11774             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11775             return cfg;
11776         }
11777         
11778         /*
11779          *  ComboBox with tickable selections
11780          */
11781              
11782         var align = this.labelAlign || this.parentLabelAlign();
11783         
11784         cfg = {
11785             cls : 'form-group roo-combobox-tickable' //input-group
11786         };
11787         
11788         var buttons = {
11789             tag : 'div',
11790             cls : 'tickable-buttons',
11791             cn : [
11792                 {
11793                     tag : 'button',
11794                     type : 'button',
11795                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11796                     html : this.triggerText
11797                 },
11798                 {
11799                     tag : 'button',
11800                     type : 'button',
11801                     name : 'ok',
11802                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11803                     html : 'Done'
11804                 },
11805                 {
11806                     tag : 'button',
11807                     type : 'button',
11808                     name : 'cancel',
11809                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11810                     html : 'Cancel'
11811                 }
11812             ]
11813         };
11814         
11815         if(this.editable){
11816             buttons.cn.unshift({
11817                 tag: 'input',
11818                 cls: 'select2-search-field-input'
11819             });
11820         }
11821         
11822         var _this = this;
11823         
11824         Roo.each(buttons.cn, function(c){
11825             if (_this.size) {
11826                 c.cls += ' btn-' + _this.size;
11827             }
11828
11829             if (_this.disabled) {
11830                 c.disabled = true;
11831             }
11832         });
11833         
11834         var box = {
11835             tag: 'div',
11836             cn: [
11837                 {
11838                     tag: 'input',
11839                     type : 'hidden',
11840                     cls: 'form-hidden-field'
11841                 },
11842                 {
11843                     tag: 'ul',
11844                     cls: 'select2-choices',
11845                     cn:[
11846                         {
11847                             tag: 'li',
11848                             cls: 'select2-search-field',
11849                             cn: [
11850
11851                                 buttons
11852                             ]
11853                         }
11854                     ]
11855                 }
11856             ]
11857         };
11858         
11859         var combobox = {
11860             cls: 'select2-container input-group select2-container-multi',
11861             cn: [
11862                 box
11863 //                {
11864 //                    tag: 'ul',
11865 //                    cls: 'typeahead typeahead-long dropdown-menu',
11866 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11867 //                }
11868             ]
11869         };
11870         
11871         if(this.hasFeedback && !this.allowBlank){
11872             
11873             var feedback = {
11874                 tag: 'span',
11875                 cls: 'glyphicon form-control-feedback'
11876             };
11877
11878             combobox.cn.push(feedback);
11879         }
11880         
11881         if (align ==='left' && this.fieldLabel.length) {
11882             
11883 //                Roo.log("left and has label");
11884                 cfg.cn = [
11885                     
11886                     {
11887                         tag: 'label',
11888                         'for' :  id,
11889                         cls : 'control-label col-sm-' + this.labelWidth,
11890                         html : this.fieldLabel
11891                         
11892                     },
11893                     {
11894                         cls : "col-sm-" + (12 - this.labelWidth), 
11895                         cn: [
11896                             combobox
11897                         ]
11898                     }
11899                     
11900                 ];
11901         } else if ( this.fieldLabel.length) {
11902 //                Roo.log(" label");
11903                  cfg.cn = [
11904                    
11905                     {
11906                         tag: 'label',
11907                         //cls : 'input-group-addon',
11908                         html : this.fieldLabel
11909                         
11910                     },
11911                     
11912                     combobox
11913                     
11914                 ];
11915
11916         } else {
11917             
11918 //                Roo.log(" no label && no align");
11919                 cfg = combobox
11920                      
11921                 
11922         }
11923          
11924         var settings=this;
11925         ['xs','sm','md','lg'].map(function(size){
11926             if (settings[size]) {
11927                 cfg.cls += ' col-' + size + '-' + settings[size];
11928             }
11929         });
11930         
11931         return cfg;
11932         
11933     },
11934     
11935     _initEventsCalled : false,
11936     
11937     // private
11938     initEvents: function()
11939     {
11940         
11941         if (this._initEventsCalled) { // as we call render... prevent looping...
11942             return;
11943         }
11944         this._initEventsCalled = true;
11945         
11946         if (!this.store) {
11947             throw "can not find store for combo";
11948         }
11949         
11950         this.store = Roo.factory(this.store, Roo.data);
11951         
11952         // if we are building from html. then this element is so complex, that we can not really
11953         // use the rendered HTML.
11954         // so we have to trash and replace the previous code.
11955         if (Roo.XComponent.build_from_html) {
11956             
11957             // remove this element....
11958             var e = this.el.dom, k=0;
11959             while (e ) { e = e.previousSibling;  ++k;}
11960
11961             this.el.remove();
11962             
11963             this.el=false;
11964             this.rendered = false;
11965             
11966             this.render(this.parent().getChildContainer(true), k);
11967             
11968             
11969             
11970         }
11971         
11972         
11973         /*
11974          * Touch Devices
11975          */
11976         
11977         if(Roo.isTouch && this.mobileTouchView){
11978             this.initTouchView();
11979             return;
11980         }
11981         
11982         if(this.tickable){
11983             this.initTickableEvents();
11984             return;
11985         }
11986         
11987         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
11988         
11989         if(this.hiddenName){
11990             
11991             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
11992             
11993             this.hiddenField.dom.value =
11994                 this.hiddenValue !== undefined ? this.hiddenValue :
11995                 this.value !== undefined ? this.value : '';
11996
11997             // prevent input submission
11998             this.el.dom.removeAttribute('name');
11999             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12000              
12001              
12002         }
12003         //if(Roo.isGecko){
12004         //    this.el.dom.setAttribute('autocomplete', 'off');
12005         //}
12006         
12007         var cls = 'x-combo-list';
12008         
12009         //this.list = new Roo.Layer({
12010         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12011         //});
12012         
12013         var _this = this;
12014         
12015         (function(){
12016             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12017             _this.list.setWidth(lw);
12018         }).defer(100);
12019         
12020         this.list.on('mouseover', this.onViewOver, this);
12021         this.list.on('mousemove', this.onViewMove, this);
12022         
12023         this.list.on('scroll', this.onViewScroll, this);
12024         
12025         /*
12026         this.list.swallowEvent('mousewheel');
12027         this.assetHeight = 0;
12028
12029         if(this.title){
12030             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12031             this.assetHeight += this.header.getHeight();
12032         }
12033
12034         this.innerList = this.list.createChild({cls:cls+'-inner'});
12035         this.innerList.on('mouseover', this.onViewOver, this);
12036         this.innerList.on('mousemove', this.onViewMove, this);
12037         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12038         
12039         if(this.allowBlank && !this.pageSize && !this.disableClear){
12040             this.footer = this.list.createChild({cls:cls+'-ft'});
12041             this.pageTb = new Roo.Toolbar(this.footer);
12042            
12043         }
12044         if(this.pageSize){
12045             this.footer = this.list.createChild({cls:cls+'-ft'});
12046             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12047                     {pageSize: this.pageSize});
12048             
12049         }
12050         
12051         if (this.pageTb && this.allowBlank && !this.disableClear) {
12052             var _this = this;
12053             this.pageTb.add(new Roo.Toolbar.Fill(), {
12054                 cls: 'x-btn-icon x-btn-clear',
12055                 text: '&#160;',
12056                 handler: function()
12057                 {
12058                     _this.collapse();
12059                     _this.clearValue();
12060                     _this.onSelect(false, -1);
12061                 }
12062             });
12063         }
12064         if (this.footer) {
12065             this.assetHeight += this.footer.getHeight();
12066         }
12067         */
12068             
12069         if(!this.tpl){
12070             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12071         }
12072
12073         this.view = new Roo.View(this.list, this.tpl, {
12074             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12075         });
12076         //this.view.wrapEl.setDisplayed(false);
12077         this.view.on('click', this.onViewClick, this);
12078         
12079         
12080         
12081         this.store.on('beforeload', this.onBeforeLoad, this);
12082         this.store.on('load', this.onLoad, this);
12083         this.store.on('loadexception', this.onLoadException, this);
12084         /*
12085         if(this.resizable){
12086             this.resizer = new Roo.Resizable(this.list,  {
12087                pinned:true, handles:'se'
12088             });
12089             this.resizer.on('resize', function(r, w, h){
12090                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12091                 this.listWidth = w;
12092                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12093                 this.restrictHeight();
12094             }, this);
12095             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12096         }
12097         */
12098         if(!this.editable){
12099             this.editable = true;
12100             this.setEditable(false);
12101         }
12102         
12103         /*
12104         
12105         if (typeof(this.events.add.listeners) != 'undefined') {
12106             
12107             this.addicon = this.wrap.createChild(
12108                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12109        
12110             this.addicon.on('click', function(e) {
12111                 this.fireEvent('add', this);
12112             }, this);
12113         }
12114         if (typeof(this.events.edit.listeners) != 'undefined') {
12115             
12116             this.editicon = this.wrap.createChild(
12117                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12118             if (this.addicon) {
12119                 this.editicon.setStyle('margin-left', '40px');
12120             }
12121             this.editicon.on('click', function(e) {
12122                 
12123                 // we fire even  if inothing is selected..
12124                 this.fireEvent('edit', this, this.lastData );
12125                 
12126             }, this);
12127         }
12128         */
12129         
12130         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12131             "up" : function(e){
12132                 this.inKeyMode = true;
12133                 this.selectPrev();
12134             },
12135
12136             "down" : function(e){
12137                 if(!this.isExpanded()){
12138                     this.onTriggerClick();
12139                 }else{
12140                     this.inKeyMode = true;
12141                     this.selectNext();
12142                 }
12143             },
12144
12145             "enter" : function(e){
12146 //                this.onViewClick();
12147                 //return true;
12148                 this.collapse();
12149                 
12150                 if(this.fireEvent("specialkey", this, e)){
12151                     this.onViewClick(false);
12152                 }
12153                 
12154                 return true;
12155             },
12156
12157             "esc" : function(e){
12158                 this.collapse();
12159             },
12160
12161             "tab" : function(e){
12162                 this.collapse();
12163                 
12164                 if(this.fireEvent("specialkey", this, e)){
12165                     this.onViewClick(false);
12166                 }
12167                 
12168                 return true;
12169             },
12170
12171             scope : this,
12172
12173             doRelay : function(foo, bar, hname){
12174                 if(hname == 'down' || this.scope.isExpanded()){
12175                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12176                 }
12177                 return true;
12178             },
12179
12180             forceKeyDown: true
12181         });
12182         
12183         
12184         this.queryDelay = Math.max(this.queryDelay || 10,
12185                 this.mode == 'local' ? 10 : 250);
12186         
12187         
12188         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12189         
12190         if(this.typeAhead){
12191             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12192         }
12193         if(this.editable !== false){
12194             this.inputEl().on("keyup", this.onKeyUp, this);
12195         }
12196         if(this.forceSelection){
12197             this.inputEl().on('blur', this.doForce, this);
12198         }
12199         
12200         if(this.multiple){
12201             this.choices = this.el.select('ul.select2-choices', true).first();
12202             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12203         }
12204     },
12205     
12206     initTickableEvents: function()
12207     {   
12208         this.createList();
12209         
12210         if(this.hiddenName){
12211             
12212             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12213             
12214             this.hiddenField.dom.value =
12215                 this.hiddenValue !== undefined ? this.hiddenValue :
12216                 this.value !== undefined ? this.value : '';
12217
12218             // prevent input submission
12219             this.el.dom.removeAttribute('name');
12220             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12221              
12222              
12223         }
12224         
12225 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12226         
12227         this.choices = this.el.select('ul.select2-choices', true).first();
12228         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12229         if(this.triggerList){
12230             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12231         }
12232          
12233         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12234         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12235         
12236         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12237         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12238         
12239         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12240         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12241         
12242         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12243         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12244         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12245         
12246         this.okBtn.hide();
12247         this.cancelBtn.hide();
12248         
12249         var _this = this;
12250         
12251         (function(){
12252             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12253             _this.list.setWidth(lw);
12254         }).defer(100);
12255         
12256         this.list.on('mouseover', this.onViewOver, this);
12257         this.list.on('mousemove', this.onViewMove, this);
12258         
12259         this.list.on('scroll', this.onViewScroll, this);
12260         
12261         if(!this.tpl){
12262             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>';
12263         }
12264
12265         this.view = new Roo.View(this.list, this.tpl, {
12266             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12267         });
12268         
12269         //this.view.wrapEl.setDisplayed(false);
12270         this.view.on('click', this.onViewClick, this);
12271         
12272         
12273         
12274         this.store.on('beforeload', this.onBeforeLoad, this);
12275         this.store.on('load', this.onLoad, this);
12276         this.store.on('loadexception', this.onLoadException, this);
12277         
12278         if(this.editable){
12279             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12280                 "up" : function(e){
12281                     this.inKeyMode = true;
12282                     this.selectPrev();
12283                 },
12284
12285                 "down" : function(e){
12286                     this.inKeyMode = true;
12287                     this.selectNext();
12288                 },
12289
12290                 "enter" : function(e){
12291                     if(this.fireEvent("specialkey", this, e)){
12292                         this.onViewClick(false);
12293                     }
12294                     
12295                     return true;
12296                 },
12297
12298                 "esc" : function(e){
12299                     this.onTickableFooterButtonClick(e, false, false);
12300                 },
12301
12302                 "tab" : function(e){
12303                     this.fireEvent("specialkey", this, e);
12304                     
12305                     this.onTickableFooterButtonClick(e, false, false);
12306                     
12307                     return true;
12308                 },
12309
12310                 scope : this,
12311
12312                 doRelay : function(e, fn, key){
12313                     if(this.scope.isExpanded()){
12314                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12315                     }
12316                     return true;
12317                 },
12318
12319                 forceKeyDown: true
12320             });
12321         }
12322         
12323         this.queryDelay = Math.max(this.queryDelay || 10,
12324                 this.mode == 'local' ? 10 : 250);
12325         
12326         
12327         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12328         
12329         if(this.typeAhead){
12330             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12331         }
12332         
12333         if(this.editable !== false){
12334             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12335         }
12336         
12337     },
12338
12339     onDestroy : function(){
12340         if(this.view){
12341             this.view.setStore(null);
12342             this.view.el.removeAllListeners();
12343             this.view.el.remove();
12344             this.view.purgeListeners();
12345         }
12346         if(this.list){
12347             this.list.dom.innerHTML  = '';
12348         }
12349         
12350         if(this.store){
12351             this.store.un('beforeload', this.onBeforeLoad, this);
12352             this.store.un('load', this.onLoad, this);
12353             this.store.un('loadexception', this.onLoadException, this);
12354         }
12355         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12356     },
12357
12358     // private
12359     fireKey : function(e){
12360         if(e.isNavKeyPress() && !this.list.isVisible()){
12361             this.fireEvent("specialkey", this, e);
12362         }
12363     },
12364
12365     // private
12366     onResize: function(w, h){
12367 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12368 //        
12369 //        if(typeof w != 'number'){
12370 //            // we do not handle it!?!?
12371 //            return;
12372 //        }
12373 //        var tw = this.trigger.getWidth();
12374 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12375 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12376 //        var x = w - tw;
12377 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12378 //            
12379 //        //this.trigger.setStyle('left', x+'px');
12380 //        
12381 //        if(this.list && this.listWidth === undefined){
12382 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12383 //            this.list.setWidth(lw);
12384 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12385 //        }
12386         
12387     
12388         
12389     },
12390
12391     /**
12392      * Allow or prevent the user from directly editing the field text.  If false is passed,
12393      * the user will only be able to select from the items defined in the dropdown list.  This method
12394      * is the runtime equivalent of setting the 'editable' config option at config time.
12395      * @param {Boolean} value True to allow the user to directly edit the field text
12396      */
12397     setEditable : function(value){
12398         if(value == this.editable){
12399             return;
12400         }
12401         this.editable = value;
12402         if(!value){
12403             this.inputEl().dom.setAttribute('readOnly', true);
12404             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12405             this.inputEl().addClass('x-combo-noedit');
12406         }else{
12407             this.inputEl().dom.setAttribute('readOnly', false);
12408             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12409             this.inputEl().removeClass('x-combo-noedit');
12410         }
12411     },
12412
12413     // private
12414     
12415     onBeforeLoad : function(combo,opts){
12416         if(!this.hasFocus){
12417             return;
12418         }
12419          if (!opts.add) {
12420             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12421          }
12422         this.restrictHeight();
12423         this.selectedIndex = -1;
12424     },
12425
12426     // private
12427     onLoad : function(){
12428         
12429         this.hasQuery = false;
12430         
12431         if(!this.hasFocus){
12432             return;
12433         }
12434         
12435         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12436             this.loading.hide();
12437         }
12438              
12439         if(this.store.getCount() > 0){
12440             this.expand();
12441             this.restrictHeight();
12442             if(this.lastQuery == this.allQuery){
12443                 if(this.editable && !this.tickable){
12444                     this.inputEl().dom.select();
12445                 }
12446                 
12447                 if(
12448                     !this.selectByValue(this.value, true) &&
12449                     this.autoFocus && 
12450                     (
12451                         !this.store.lastOptions ||
12452                         typeof(this.store.lastOptions.add) == 'undefined' || 
12453                         this.store.lastOptions.add != true
12454                     )
12455                 ){
12456                     this.select(0, true);
12457                 }
12458             }else{
12459                 if(this.autoFocus){
12460                     this.selectNext();
12461                 }
12462                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12463                     this.taTask.delay(this.typeAheadDelay);
12464                 }
12465             }
12466         }else{
12467             this.onEmptyResults();
12468         }
12469         
12470         //this.el.focus();
12471     },
12472     // private
12473     onLoadException : function()
12474     {
12475         this.hasQuery = false;
12476         
12477         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12478             this.loading.hide();
12479         }
12480         
12481         if(this.tickable && this.editable){
12482             return;
12483         }
12484         
12485         this.collapse();
12486         // only causes errors at present
12487         //Roo.log(this.store.reader.jsonData);
12488         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12489             // fixme
12490             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12491         //}
12492         
12493         
12494     },
12495     // private
12496     onTypeAhead : function(){
12497         if(this.store.getCount() > 0){
12498             var r = this.store.getAt(0);
12499             var newValue = r.data[this.displayField];
12500             var len = newValue.length;
12501             var selStart = this.getRawValue().length;
12502             
12503             if(selStart != len){
12504                 this.setRawValue(newValue);
12505                 this.selectText(selStart, newValue.length);
12506             }
12507         }
12508     },
12509
12510     // private
12511     onSelect : function(record, index){
12512         
12513         if(this.fireEvent('beforeselect', this, record, index) !== false){
12514         
12515             this.setFromData(index > -1 ? record.data : false);
12516             
12517             this.collapse();
12518             this.fireEvent('select', this, record, index);
12519         }
12520     },
12521
12522     /**
12523      * Returns the currently selected field value or empty string if no value is set.
12524      * @return {String} value The selected value
12525      */
12526     getValue : function(){
12527         
12528         if(this.multiple){
12529             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12530         }
12531         
12532         if(this.valueField){
12533             return typeof this.value != 'undefined' ? this.value : '';
12534         }else{
12535             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12536         }
12537     },
12538
12539     /**
12540      * Clears any text/value currently set in the field
12541      */
12542     clearValue : function(){
12543         if(this.hiddenField){
12544             this.hiddenField.dom.value = '';
12545         }
12546         this.value = '';
12547         this.setRawValue('');
12548         this.lastSelectionText = '';
12549         this.lastData = false;
12550         
12551         var close = this.closeTriggerEl();
12552         
12553         if(close){
12554             close.hide();
12555         }
12556         
12557     },
12558
12559     /**
12560      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12561      * will be displayed in the field.  If the value does not match the data value of an existing item,
12562      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12563      * Otherwise the field will be blank (although the value will still be set).
12564      * @param {String} value The value to match
12565      */
12566     setValue : function(v){
12567         if(this.multiple){
12568             this.syncValue();
12569             return;
12570         }
12571         
12572         var text = v;
12573         if(this.valueField){
12574             var r = this.findRecord(this.valueField, v);
12575             if(r){
12576                 text = r.data[this.displayField];
12577             }else if(this.valueNotFoundText !== undefined){
12578                 text = this.valueNotFoundText;
12579             }
12580         }
12581         this.lastSelectionText = text;
12582         if(this.hiddenField){
12583             this.hiddenField.dom.value = v;
12584         }
12585         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12586         this.value = v;
12587         
12588         var close = this.closeTriggerEl();
12589         
12590         if(close){
12591             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12592         }
12593     },
12594     /**
12595      * @property {Object} the last set data for the element
12596      */
12597     
12598     lastData : false,
12599     /**
12600      * Sets the value of the field based on a object which is related to the record format for the store.
12601      * @param {Object} value the value to set as. or false on reset?
12602      */
12603     setFromData : function(o){
12604         
12605         if(this.multiple){
12606             this.addItem(o);
12607             return;
12608         }
12609             
12610         var dv = ''; // display value
12611         var vv = ''; // value value..
12612         this.lastData = o;
12613         if (this.displayField) {
12614             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12615         } else {
12616             // this is an error condition!!!
12617             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12618         }
12619         
12620         if(this.valueField){
12621             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12622         }
12623         
12624         var close = this.closeTriggerEl();
12625         
12626         if(close){
12627             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12628         }
12629         
12630         if(this.hiddenField){
12631             this.hiddenField.dom.value = vv;
12632             
12633             this.lastSelectionText = dv;
12634             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12635             this.value = vv;
12636             return;
12637         }
12638         // no hidden field.. - we store the value in 'value', but still display
12639         // display field!!!!
12640         this.lastSelectionText = dv;
12641         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12642         this.value = vv;
12643         
12644         
12645         
12646     },
12647     // private
12648     reset : function(){
12649         // overridden so that last data is reset..
12650         
12651         if(this.multiple){
12652             this.clearItem();
12653             return;
12654         }
12655         
12656         this.setValue(this.originalValue);
12657         this.clearInvalid();
12658         this.lastData = false;
12659         if (this.view) {
12660             this.view.clearSelections();
12661         }
12662     },
12663     // private
12664     findRecord : function(prop, value){
12665         var record;
12666         if(this.store.getCount() > 0){
12667             this.store.each(function(r){
12668                 if(r.data[prop] == value){
12669                     record = r;
12670                     return false;
12671                 }
12672                 return true;
12673             });
12674         }
12675         return record;
12676     },
12677     
12678     getName: function()
12679     {
12680         // returns hidden if it's set..
12681         if (!this.rendered) {return ''};
12682         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12683         
12684     },
12685     // private
12686     onViewMove : function(e, t){
12687         this.inKeyMode = false;
12688     },
12689
12690     // private
12691     onViewOver : function(e, t){
12692         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12693             return;
12694         }
12695         var item = this.view.findItemFromChild(t);
12696         
12697         if(item){
12698             var index = this.view.indexOf(item);
12699             this.select(index, false);
12700         }
12701     },
12702
12703     // private
12704     onViewClick : function(view, doFocus, el, e)
12705     {
12706         var index = this.view.getSelectedIndexes()[0];
12707         
12708         var r = this.store.getAt(index);
12709         
12710         if(this.tickable){
12711             
12712             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12713                 return;
12714             }
12715             
12716             var rm = false;
12717             var _this = this;
12718             
12719             Roo.each(this.tickItems, function(v,k){
12720                 
12721                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12722                     Roo.log(v);
12723                     _this.tickItems.splice(k, 1);
12724                     
12725                     if(typeof(e) == 'undefined' && view == false){
12726                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12727                     }
12728                     
12729                     rm = true;
12730                     return;
12731                 }
12732             });
12733             
12734             if(rm){
12735                 return;
12736             }
12737             
12738             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12739                 this.tickItems.push(r.data);
12740             }
12741             
12742             if(typeof(e) == 'undefined' && view == false){
12743                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12744             }
12745                     
12746             return;
12747         }
12748         
12749         if(r){
12750             this.onSelect(r, index);
12751         }
12752         if(doFocus !== false && !this.blockFocus){
12753             this.inputEl().focus();
12754         }
12755     },
12756
12757     // private
12758     restrictHeight : function(){
12759         //this.innerList.dom.style.height = '';
12760         //var inner = this.innerList.dom;
12761         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12762         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12763         //this.list.beginUpdate();
12764         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12765         this.list.alignTo(this.inputEl(), this.listAlign);
12766         this.list.alignTo(this.inputEl(), this.listAlign);
12767         //this.list.endUpdate();
12768     },
12769
12770     // private
12771     onEmptyResults : function(){
12772         
12773         if(this.tickable && this.editable){
12774             this.restrictHeight();
12775             return;
12776         }
12777         
12778         this.collapse();
12779     },
12780
12781     /**
12782      * Returns true if the dropdown list is expanded, else false.
12783      */
12784     isExpanded : function(){
12785         return this.list.isVisible();
12786     },
12787
12788     /**
12789      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12790      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12791      * @param {String} value The data value of the item to select
12792      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12793      * selected item if it is not currently in view (defaults to true)
12794      * @return {Boolean} True if the value matched an item in the list, else false
12795      */
12796     selectByValue : function(v, scrollIntoView){
12797         if(v !== undefined && v !== null){
12798             var r = this.findRecord(this.valueField || this.displayField, v);
12799             if(r){
12800                 this.select(this.store.indexOf(r), scrollIntoView);
12801                 return true;
12802             }
12803         }
12804         return false;
12805     },
12806
12807     /**
12808      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12809      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12810      * @param {Number} index The zero-based index of the list item to select
12811      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12812      * selected item if it is not currently in view (defaults to true)
12813      */
12814     select : function(index, scrollIntoView){
12815         this.selectedIndex = index;
12816         this.view.select(index);
12817         if(scrollIntoView !== false){
12818             var el = this.view.getNode(index);
12819             /*
12820              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12821              */
12822             if(el){
12823                 this.list.scrollChildIntoView(el, false);
12824             }
12825         }
12826     },
12827
12828     // private
12829     selectNext : function(){
12830         var ct = this.store.getCount();
12831         if(ct > 0){
12832             if(this.selectedIndex == -1){
12833                 this.select(0);
12834             }else if(this.selectedIndex < ct-1){
12835                 this.select(this.selectedIndex+1);
12836             }
12837         }
12838     },
12839
12840     // private
12841     selectPrev : function(){
12842         var ct = this.store.getCount();
12843         if(ct > 0){
12844             if(this.selectedIndex == -1){
12845                 this.select(0);
12846             }else if(this.selectedIndex != 0){
12847                 this.select(this.selectedIndex-1);
12848             }
12849         }
12850     },
12851
12852     // private
12853     onKeyUp : function(e){
12854         if(this.editable !== false && !e.isSpecialKey()){
12855             this.lastKey = e.getKey();
12856             this.dqTask.delay(this.queryDelay);
12857         }
12858     },
12859
12860     // private
12861     validateBlur : function(){
12862         return !this.list || !this.list.isVisible();   
12863     },
12864
12865     // private
12866     initQuery : function(){
12867         
12868         var v = this.getRawValue();
12869         
12870         if(this.tickable && this.editable){
12871             v = this.tickableInputEl().getValue();
12872         }
12873         
12874         this.doQuery(v);
12875     },
12876
12877     // private
12878     doForce : function(){
12879         if(this.inputEl().dom.value.length > 0){
12880             this.inputEl().dom.value =
12881                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12882              
12883         }
12884     },
12885
12886     /**
12887      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12888      * query allowing the query action to be canceled if needed.
12889      * @param {String} query The SQL query to execute
12890      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12891      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12892      * saved in the current store (defaults to false)
12893      */
12894     doQuery : function(q, forceAll){
12895         
12896         if(q === undefined || q === null){
12897             q = '';
12898         }
12899         var qe = {
12900             query: q,
12901             forceAll: forceAll,
12902             combo: this,
12903             cancel:false
12904         };
12905         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12906             return false;
12907         }
12908         q = qe.query;
12909         
12910         forceAll = qe.forceAll;
12911         if(forceAll === true || (q.length >= this.minChars)){
12912             
12913             this.hasQuery = true;
12914             
12915             if(this.lastQuery != q || this.alwaysQuery){
12916                 this.lastQuery = q;
12917                 if(this.mode == 'local'){
12918                     this.selectedIndex = -1;
12919                     if(forceAll){
12920                         this.store.clearFilter();
12921                     }else{
12922                         
12923                         if(this.specialFilter){
12924                             this.fireEvent('specialfilter', this);
12925                             this.onLoad();
12926                             return;
12927                         }
12928                         
12929                         this.store.filter(this.displayField, q);
12930                     }
12931                     
12932                     this.store.fireEvent("datachanged", this.store);
12933                     
12934                     this.onLoad();
12935                     
12936                     
12937                 }else{
12938                     
12939                     this.store.baseParams[this.queryParam] = q;
12940                     
12941                     var options = {params : this.getParams(q)};
12942                     
12943                     if(this.loadNext){
12944                         options.add = true;
12945                         options.params.start = this.page * this.pageSize;
12946                     }
12947                     
12948                     this.store.load(options);
12949                     
12950                     /*
12951                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
12952                      *  we should expand the list on onLoad
12953                      *  so command out it
12954                      */
12955 //                    this.expand();
12956                 }
12957             }else{
12958                 this.selectedIndex = -1;
12959                 this.onLoad();   
12960             }
12961         }
12962         
12963         this.loadNext = false;
12964     },
12965     
12966     // private
12967     getParams : function(q){
12968         var p = {};
12969         //p[this.queryParam] = q;
12970         
12971         if(this.pageSize){
12972             p.start = 0;
12973             p.limit = this.pageSize;
12974         }
12975         return p;
12976     },
12977
12978     /**
12979      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
12980      */
12981     collapse : function(){
12982         if(!this.isExpanded()){
12983             return;
12984         }
12985         
12986         this.list.hide();
12987         
12988         if(this.tickable){
12989             this.hasFocus = false;
12990             this.okBtn.hide();
12991             this.cancelBtn.hide();
12992             this.trigger.show();
12993             
12994             if(this.editable){
12995                 this.tickableInputEl().dom.value = '';
12996                 this.tickableInputEl().blur();
12997             }
12998             
12999         }
13000         
13001         Roo.get(document).un('mousedown', this.collapseIf, this);
13002         Roo.get(document).un('mousewheel', this.collapseIf, this);
13003         if (!this.editable) {
13004             Roo.get(document).un('keydown', this.listKeyPress, this);
13005         }
13006         this.fireEvent('collapse', this);
13007     },
13008
13009     // private
13010     collapseIf : function(e){
13011         var in_combo  = e.within(this.el);
13012         var in_list =  e.within(this.list);
13013         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13014         
13015         if (in_combo || in_list || is_list) {
13016             //e.stopPropagation();
13017             return;
13018         }
13019         
13020         if(this.tickable){
13021             this.onTickableFooterButtonClick(e, false, false);
13022         }
13023
13024         this.collapse();
13025         
13026     },
13027
13028     /**
13029      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13030      */
13031     expand : function(){
13032        
13033         if(this.isExpanded() || !this.hasFocus){
13034             return;
13035         }
13036         
13037         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13038         this.list.setWidth(lw);
13039         
13040         
13041          Roo.log('expand');
13042         
13043         this.list.show();
13044         
13045         this.restrictHeight();
13046         
13047         if(this.tickable){
13048             
13049             this.tickItems = Roo.apply([], this.item);
13050             
13051             this.okBtn.show();
13052             this.cancelBtn.show();
13053             this.trigger.hide();
13054             
13055             if(this.editable){
13056                 this.tickableInputEl().focus();
13057             }
13058             
13059         }
13060         
13061         Roo.get(document).on('mousedown', this.collapseIf, this);
13062         Roo.get(document).on('mousewheel', this.collapseIf, this);
13063         if (!this.editable) {
13064             Roo.get(document).on('keydown', this.listKeyPress, this);
13065         }
13066         
13067         this.fireEvent('expand', this);
13068     },
13069
13070     // private
13071     // Implements the default empty TriggerField.onTriggerClick function
13072     onTriggerClick : function(e)
13073     {
13074         Roo.log('trigger click');
13075         
13076         if(this.disabled || !this.triggerList){
13077             return;
13078         }
13079         
13080         this.page = 0;
13081         this.loadNext = false;
13082         
13083         if(this.isExpanded()){
13084             this.collapse();
13085             if (!this.blockFocus) {
13086                 this.inputEl().focus();
13087             }
13088             
13089         }else {
13090             this.hasFocus = true;
13091             if(this.triggerAction == 'all') {
13092                 this.doQuery(this.allQuery, true);
13093             } else {
13094                 this.doQuery(this.getRawValue());
13095             }
13096             if (!this.blockFocus) {
13097                 this.inputEl().focus();
13098             }
13099         }
13100     },
13101     
13102     onTickableTriggerClick : function(e)
13103     {
13104         if(this.disabled){
13105             return;
13106         }
13107         
13108         this.page = 0;
13109         this.loadNext = false;
13110         this.hasFocus = true;
13111         
13112         if(this.triggerAction == 'all') {
13113             this.doQuery(this.allQuery, true);
13114         } else {
13115             this.doQuery(this.getRawValue());
13116         }
13117     },
13118     
13119     onSearchFieldClick : function(e)
13120     {
13121         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13122             this.onTickableFooterButtonClick(e, false, false);
13123             return;
13124         }
13125         
13126         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13127             return;
13128         }
13129         
13130         this.page = 0;
13131         this.loadNext = false;
13132         this.hasFocus = true;
13133         
13134         if(this.triggerAction == 'all') {
13135             this.doQuery(this.allQuery, true);
13136         } else {
13137             this.doQuery(this.getRawValue());
13138         }
13139     },
13140     
13141     listKeyPress : function(e)
13142     {
13143         //Roo.log('listkeypress');
13144         // scroll to first matching element based on key pres..
13145         if (e.isSpecialKey()) {
13146             return false;
13147         }
13148         var k = String.fromCharCode(e.getKey()).toUpperCase();
13149         //Roo.log(k);
13150         var match  = false;
13151         var csel = this.view.getSelectedNodes();
13152         var cselitem = false;
13153         if (csel.length) {
13154             var ix = this.view.indexOf(csel[0]);
13155             cselitem  = this.store.getAt(ix);
13156             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13157                 cselitem = false;
13158             }
13159             
13160         }
13161         
13162         this.store.each(function(v) { 
13163             if (cselitem) {
13164                 // start at existing selection.
13165                 if (cselitem.id == v.id) {
13166                     cselitem = false;
13167                 }
13168                 return true;
13169             }
13170                 
13171             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13172                 match = this.store.indexOf(v);
13173                 return false;
13174             }
13175             return true;
13176         }, this);
13177         
13178         if (match === false) {
13179             return true; // no more action?
13180         }
13181         // scroll to?
13182         this.view.select(match);
13183         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13184         sn.scrollIntoView(sn.dom.parentNode, false);
13185     },
13186     
13187     onViewScroll : function(e, t){
13188         
13189         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){
13190             return;
13191         }
13192         
13193         this.hasQuery = true;
13194         
13195         this.loading = this.list.select('.loading', true).first();
13196         
13197         if(this.loading === null){
13198             this.list.createChild({
13199                 tag: 'div',
13200                 cls: 'loading select2-more-results select2-active',
13201                 html: 'Loading more results...'
13202             });
13203             
13204             this.loading = this.list.select('.loading', true).first();
13205             
13206             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13207             
13208             this.loading.hide();
13209         }
13210         
13211         this.loading.show();
13212         
13213         var _combo = this;
13214         
13215         this.page++;
13216         this.loadNext = true;
13217         
13218         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13219         
13220         return;
13221     },
13222     
13223     addItem : function(o)
13224     {   
13225         var dv = ''; // display value
13226         
13227         if (this.displayField) {
13228             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13229         } else {
13230             // this is an error condition!!!
13231             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13232         }
13233         
13234         if(!dv.length){
13235             return;
13236         }
13237         
13238         var choice = this.choices.createChild({
13239             tag: 'li',
13240             cls: 'select2-search-choice',
13241             cn: [
13242                 {
13243                     tag: 'div',
13244                     html: dv
13245                 },
13246                 {
13247                     tag: 'a',
13248                     href: '#',
13249                     cls: 'select2-search-choice-close',
13250                     tabindex: '-1'
13251                 }
13252             ]
13253             
13254         }, this.searchField);
13255         
13256         var close = choice.select('a.select2-search-choice-close', true).first();
13257         
13258         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13259         
13260         this.item.push(o);
13261         
13262         this.lastData = o;
13263         
13264         this.syncValue();
13265         
13266         this.inputEl().dom.value = '';
13267         
13268         this.validate();
13269     },
13270     
13271     onRemoveItem : function(e, _self, o)
13272     {
13273         e.preventDefault();
13274         
13275         this.lastItem = Roo.apply([], this.item);
13276         
13277         var index = this.item.indexOf(o.data) * 1;
13278         
13279         if( index < 0){
13280             Roo.log('not this item?!');
13281             return;
13282         }
13283         
13284         this.item.splice(index, 1);
13285         o.item.remove();
13286         
13287         this.syncValue();
13288         
13289         this.fireEvent('remove', this, e);
13290         
13291         this.validate();
13292         
13293     },
13294     
13295     syncValue : function()
13296     {
13297         if(!this.item.length){
13298             this.clearValue();
13299             return;
13300         }
13301             
13302         var value = [];
13303         var _this = this;
13304         Roo.each(this.item, function(i){
13305             if(_this.valueField){
13306                 value.push(i[_this.valueField]);
13307                 return;
13308             }
13309
13310             value.push(i);
13311         });
13312
13313         this.value = value.join(',');
13314
13315         if(this.hiddenField){
13316             this.hiddenField.dom.value = this.value;
13317         }
13318         
13319         this.store.fireEvent("datachanged", this.store);
13320     },
13321     
13322     clearItem : function()
13323     {
13324         if(!this.multiple){
13325             return;
13326         }
13327         
13328         this.item = [];
13329         
13330         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13331            c.remove();
13332         });
13333         
13334         this.syncValue();
13335         
13336         this.validate();
13337         
13338         if(this.tickable && !Roo.isTouch){
13339             this.view.refresh();
13340         }
13341     },
13342     
13343     inputEl: function ()
13344     {
13345         if(Roo.isTouch && this.mobileTouchView){
13346             return this.el.select('input.form-control',true).first();
13347         }
13348         
13349         if(this.tickable){
13350             return this.searchField;
13351         }
13352         
13353         return this.el.select('input.form-control',true).first();
13354     },
13355     
13356     
13357     onTickableFooterButtonClick : function(e, btn, el)
13358     {
13359         e.preventDefault();
13360         
13361         this.lastItem = Roo.apply([], this.item);
13362         
13363         if(btn && btn.name == 'cancel'){
13364             this.tickItems = Roo.apply([], this.item);
13365             this.collapse();
13366             return;
13367         }
13368         
13369         this.clearItem();
13370         
13371         var _this = this;
13372         
13373         Roo.each(this.tickItems, function(o){
13374             _this.addItem(o);
13375         });
13376         
13377         this.collapse();
13378         
13379     },
13380     
13381     validate : function()
13382     {
13383         var v = this.getRawValue();
13384         
13385         if(this.multiple){
13386             v = this.getValue();
13387         }
13388         
13389         if(this.disabled || this.allowBlank || v.length){
13390             this.markValid();
13391             return true;
13392         }
13393         
13394         this.markInvalid();
13395         return false;
13396     },
13397     
13398     tickableInputEl : function()
13399     {
13400         if(!this.tickable || !this.editable){
13401             return this.inputEl();
13402         }
13403         
13404         return this.inputEl().select('.select2-search-field-input', true).first();
13405     },
13406     
13407     
13408     getAutoCreateTouchView : function()
13409     {
13410         var id = Roo.id();
13411         
13412         var cfg = {
13413             cls: 'form-group' //input-group
13414         };
13415         
13416         var input =  {
13417             tag: 'input',
13418             id : id,
13419             type : this.inputType,
13420             cls : 'form-control x-combo-noedit',
13421             autocomplete: 'new-password',
13422             placeholder : this.placeholder || '',
13423             readonly : true
13424         };
13425         
13426         if (this.name) {
13427             input.name = this.name;
13428         }
13429         
13430         if (this.size) {
13431             input.cls += ' input-' + this.size;
13432         }
13433         
13434         if (this.disabled) {
13435             input.disabled = true;
13436         }
13437         
13438         var inputblock = {
13439             cls : '',
13440             cn : [
13441                 input
13442             ]
13443         };
13444         
13445         if(this.before){
13446             inputblock.cls += ' input-group';
13447             
13448             inputblock.cn.unshift({
13449                 tag :'span',
13450                 cls : 'input-group-addon',
13451                 html : this.before
13452             });
13453         }
13454         
13455         if(this.removable && !this.multiple){
13456             inputblock.cls += ' roo-removable';
13457             
13458             inputblock.cn.push({
13459                 tag: 'button',
13460                 html : 'x',
13461                 cls : 'roo-combo-removable-btn close'
13462             });
13463         }
13464
13465         if(this.hasFeedback && !this.allowBlank){
13466             
13467             inputblock.cls += ' has-feedback';
13468             
13469             inputblock.cn.push({
13470                 tag: 'span',
13471                 cls: 'glyphicon form-control-feedback'
13472             });
13473             
13474         }
13475         
13476         if (this.after) {
13477             
13478             inputblock.cls += (this.before) ? '' : ' input-group';
13479             
13480             inputblock.cn.push({
13481                 tag :'span',
13482                 cls : 'input-group-addon',
13483                 html : this.after
13484             });
13485         }
13486
13487         var box = {
13488             tag: 'div',
13489             cn: [
13490                 {
13491                     tag: 'input',
13492                     type : 'hidden',
13493                     cls: 'form-hidden-field'
13494                 },
13495                 inputblock
13496             ]
13497             
13498         };
13499         
13500         if(this.multiple){
13501             box = {
13502                 tag: 'div',
13503                 cn: [
13504                     {
13505                         tag: 'input',
13506                         type : 'hidden',
13507                         cls: 'form-hidden-field'
13508                     },
13509                     {
13510                         tag: 'ul',
13511                         cls: 'select2-choices',
13512                         cn:[
13513                             {
13514                                 tag: 'li',
13515                                 cls: 'select2-search-field',
13516                                 cn: [
13517
13518                                     inputblock
13519                                 ]
13520                             }
13521                         ]
13522                     }
13523                 ]
13524             }
13525         };
13526         
13527         var combobox = {
13528             cls: 'select2-container input-group',
13529             cn: [
13530                 box
13531             ]
13532         };
13533         
13534         if(this.multiple){
13535             combobox.cls += ' select2-container-multi';
13536         }
13537         
13538         var align = this.labelAlign || this.parentLabelAlign();
13539         
13540         cfg.cn = combobox;
13541         
13542         if(this.fieldLabel.length){
13543             
13544             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13545             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13546             
13547             cfg.cn = [
13548                 {
13549                     tag: 'label',
13550                     cls : 'control-label ' + lw,
13551                     html : this.fieldLabel
13552
13553                 },
13554                 {
13555                     cls : cw, 
13556                     cn: [
13557                         combobox
13558                     ]
13559                 }
13560             ];
13561         }
13562         
13563         var settings = this;
13564         
13565         ['xs','sm','md','lg'].map(function(size){
13566             if (settings[size]) {
13567                 cfg.cls += ' col-' + size + '-' + settings[size];
13568             }
13569         });
13570         
13571         return cfg;
13572     },
13573     
13574     initTouchView : function()
13575     {
13576         this.renderTouchView();
13577         
13578         this.touchViewEl.on('scroll', function(){
13579             this.el.dom.scrollTop = 0;
13580         }, this);
13581         
13582         this.originalValue = this.getValue();
13583         
13584         this.inputEl().on("click", this.showTouchView, this);
13585         
13586         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13587         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13588         
13589         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13590         
13591         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13592         this.store.on('load', this.onTouchViewLoad, this);
13593         this.store.on('loadexception', this.onTouchViewLoadException, this);
13594         
13595         if(this.hiddenName){
13596             
13597             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13598             
13599             this.hiddenField.dom.value =
13600                 this.hiddenValue !== undefined ? this.hiddenValue :
13601                 this.value !== undefined ? this.value : '';
13602         
13603             this.el.dom.removeAttribute('name');
13604             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13605         }
13606         
13607         if(this.multiple){
13608             this.choices = this.el.select('ul.select2-choices', true).first();
13609             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13610         }
13611         
13612         if(this.removable && !this.multiple){
13613             var close = this.closeTriggerEl();
13614             if(close){
13615                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13616                 close.on('click', this.removeBtnClick, this, close);
13617             }
13618         }
13619         /*
13620          * fix the bug in Safari iOS8
13621          */
13622         this.inputEl().on("focus", function(e){
13623             document.activeElement.blur();
13624         }, this);
13625         
13626         return;
13627         
13628         
13629     },
13630     
13631     renderTouchView : function()
13632     {
13633         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13634         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13635         
13636         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13637         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13638         
13639         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13640         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13641         this.touchViewBodyEl.setStyle('overflow', 'auto');
13642         
13643         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13644         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13645         
13646         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13647         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13648         
13649     },
13650     
13651     showTouchView : function()
13652     {
13653         if(this.disabled){
13654             return;
13655         }
13656         
13657         this.touchViewHeaderEl.hide();
13658
13659         if(this.fieldLabel.length){
13660             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13661             this.touchViewHeaderEl.show();
13662         }
13663
13664         this.touchViewEl.show();
13665
13666         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13667         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13668
13669         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13670
13671         if(this.fieldLabel.length){
13672             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13673         }
13674         
13675         this.touchViewBodyEl.setHeight(bodyHeight);
13676
13677         if(this.animate){
13678             var _this = this;
13679             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13680         }else{
13681             this.touchViewEl.addClass('in');
13682         }
13683
13684         this.doTouchViewQuery();
13685         
13686     },
13687     
13688     hideTouchView : function()
13689     {
13690         this.touchViewEl.removeClass('in');
13691
13692         if(this.animate){
13693             var _this = this;
13694             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13695         }else{
13696             this.touchViewEl.setStyle('display', 'none');
13697         }
13698         
13699     },
13700     
13701     setTouchViewValue : function()
13702     {
13703         if(this.multiple){
13704             this.clearItem();
13705         
13706             var _this = this;
13707
13708             Roo.each(this.tickItems, function(o){
13709                 this.addItem(o);
13710             }, this);
13711         }
13712         
13713         this.hideTouchView();
13714     },
13715     
13716     doTouchViewQuery : function()
13717     {
13718         var qe = {
13719             query: '',
13720             forceAll: true,
13721             combo: this,
13722             cancel:false
13723         };
13724         
13725         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13726             return false;
13727         }
13728         
13729         if(!this.alwaysQuery || this.mode == 'local'){
13730             this.onTouchViewLoad();
13731             return;
13732         }
13733         
13734         this.store.load();
13735     },
13736     
13737     onTouchViewBeforeLoad : function(combo,opts)
13738     {
13739         return;
13740     },
13741
13742     // private
13743     onTouchViewLoad : function()
13744     {
13745         if(this.store.getCount() < 1){
13746             this.onTouchViewEmptyResults();
13747             return;
13748         }
13749         
13750         this.clearTouchView();
13751         
13752         var rawValue = this.getRawValue();
13753         
13754         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13755         
13756         this.tickItems = [];
13757         
13758         this.store.data.each(function(d, rowIndex){
13759             var row = this.touchViewListGroup.createChild(template);
13760             
13761             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13762                 var cfg = {
13763                     data : d.data,
13764                     html : d.data[this.displayField]
13765                 };
13766                 
13767                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13768                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13769                 }
13770             }
13771             
13772             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13773                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13774             }
13775             
13776             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13777                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13778                 this.tickItems.push(d.data);
13779             }
13780             
13781             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13782             
13783         }, this);
13784         
13785         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13786         
13787         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13788
13789         if(this.fieldLabel.length){
13790             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13791         }
13792
13793         var listHeight = this.touchViewListGroup.getHeight();
13794         
13795         var _this = this;
13796         
13797         if(firstChecked && listHeight > bodyHeight){
13798             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13799         }
13800         
13801     },
13802     
13803     onTouchViewLoadException : function()
13804     {
13805         this.hideTouchView();
13806     },
13807     
13808     onTouchViewEmptyResults : function()
13809     {
13810         this.clearTouchView();
13811         
13812         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13813         
13814         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13815         
13816     },
13817     
13818     clearTouchView : function()
13819     {
13820         this.touchViewListGroup.dom.innerHTML = '';
13821     },
13822     
13823     onTouchViewClick : function(e, el, o)
13824     {
13825         e.preventDefault();
13826         
13827         var row = o.row;
13828         var rowIndex = o.rowIndex;
13829         
13830         var r = this.store.getAt(rowIndex);
13831         
13832         if(!this.multiple){
13833             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13834                 c.dom.removeAttribute('checked');
13835             }, this);
13836             
13837             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13838         
13839             this.setFromData(r.data);
13840             
13841             var close = this.closeTriggerEl();
13842         
13843             if(close){
13844                 close.show();
13845             }
13846
13847             this.hideTouchView();
13848             
13849             this.fireEvent('select', this, r, rowIndex);
13850             
13851             return;
13852         }
13853         
13854         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13855             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13856             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13857             return;
13858         }
13859         
13860         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13861         this.addItem(r.data);
13862         this.tickItems.push(r.data);
13863         
13864     }
13865     
13866
13867     /** 
13868     * @cfg {Boolean} grow 
13869     * @hide 
13870     */
13871     /** 
13872     * @cfg {Number} growMin 
13873     * @hide 
13874     */
13875     /** 
13876     * @cfg {Number} growMax 
13877     * @hide 
13878     */
13879     /**
13880      * @hide
13881      * @method autoSize
13882      */
13883 });
13884
13885 Roo.apply(Roo.bootstrap.ComboBox,  {
13886     
13887     header : {
13888         tag: 'div',
13889         cls: 'modal-header',
13890         cn: [
13891             {
13892                 tag: 'h4',
13893                 cls: 'modal-title'
13894             }
13895         ]
13896     },
13897     
13898     body : {
13899         tag: 'div',
13900         cls: 'modal-body',
13901         cn: [
13902             {
13903                 tag: 'ul',
13904                 cls: 'list-group'
13905             }
13906         ]
13907     },
13908     
13909     listItemRadio : {
13910         tag: 'li',
13911         cls: 'list-group-item',
13912         cn: [
13913             {
13914                 tag: 'span',
13915                 cls: 'roo-combobox-list-group-item-value'
13916             },
13917             {
13918                 tag: 'div',
13919                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13920                 cn: [
13921                     {
13922                         tag: 'input',
13923                         type: 'radio'
13924                     },
13925                     {
13926                         tag: 'label'
13927                     }
13928                 ]
13929             }
13930         ]
13931     },
13932     
13933     listItemCheckbox : {
13934         tag: 'li',
13935         cls: 'list-group-item',
13936         cn: [
13937             {
13938                 tag: 'span',
13939                 cls: 'roo-combobox-list-group-item-value'
13940             },
13941             {
13942                 tag: 'div',
13943                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
13944                 cn: [
13945                     {
13946                         tag: 'input',
13947                         type: 'checkbox'
13948                     },
13949                     {
13950                         tag: 'label'
13951                     }
13952                 ]
13953             }
13954         ]
13955     },
13956     
13957     emptyResult : {
13958         tag: 'div',
13959         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
13960     },
13961     
13962     footer : {
13963         tag: 'div',
13964         cls: 'modal-footer',
13965         cn: [
13966             {
13967                 tag: 'div',
13968                 cls: 'row',
13969                 cn: [
13970                     {
13971                         tag: 'div',
13972                         cls: 'col-xs-6 text-left',
13973                         cn: {
13974                             tag: 'button',
13975                             cls: 'btn btn-danger roo-touch-view-cancel',
13976                             html: 'Cancel'
13977                         }
13978                     },
13979                     {
13980                         tag: 'div',
13981                         cls: 'col-xs-6 text-right',
13982                         cn: {
13983                             tag: 'button',
13984                             cls: 'btn btn-success roo-touch-view-ok',
13985                             html: 'OK'
13986                         }
13987                     }
13988                 ]
13989             }
13990         ]
13991         
13992     }
13993 });
13994
13995 Roo.apply(Roo.bootstrap.ComboBox,  {
13996     
13997     touchViewTemplate : {
13998         tag: 'div',
13999         cls: 'modal fade roo-combobox-touch-view',
14000         cn: [
14001             {
14002                 tag: 'div',
14003                 cls: 'modal-dialog',
14004                 style : 'position:fixed', // we have to fix position....
14005                 cn: [
14006                     {
14007                         tag: 'div',
14008                         cls: 'modal-content',
14009                         cn: [
14010                             Roo.bootstrap.ComboBox.header,
14011                             Roo.bootstrap.ComboBox.body,
14012                             Roo.bootstrap.ComboBox.footer
14013                         ]
14014                     }
14015                 ]
14016             }
14017         ]
14018     }
14019 });/*
14020  * Based on:
14021  * Ext JS Library 1.1.1
14022  * Copyright(c) 2006-2007, Ext JS, LLC.
14023  *
14024  * Originally Released Under LGPL - original licence link has changed is not relivant.
14025  *
14026  * Fork - LGPL
14027  * <script type="text/javascript">
14028  */
14029
14030 /**
14031  * @class Roo.View
14032  * @extends Roo.util.Observable
14033  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14034  * This class also supports single and multi selection modes. <br>
14035  * Create a data model bound view:
14036  <pre><code>
14037  var store = new Roo.data.Store(...);
14038
14039  var view = new Roo.View({
14040     el : "my-element",
14041     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14042  
14043     singleSelect: true,
14044     selectedClass: "ydataview-selected",
14045     store: store
14046  });
14047
14048  // listen for node click?
14049  view.on("click", function(vw, index, node, e){
14050  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14051  });
14052
14053  // load XML data
14054  dataModel.load("foobar.xml");
14055  </code></pre>
14056  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14057  * <br><br>
14058  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14059  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14060  * 
14061  * Note: old style constructor is still suported (container, template, config)
14062  * 
14063  * @constructor
14064  * Create a new View
14065  * @param {Object} config The config object
14066  * 
14067  */
14068 Roo.View = function(config, depreciated_tpl, depreciated_config){
14069     
14070     this.parent = false;
14071     
14072     if (typeof(depreciated_tpl) == 'undefined') {
14073         // new way.. - universal constructor.
14074         Roo.apply(this, config);
14075         this.el  = Roo.get(this.el);
14076     } else {
14077         // old format..
14078         this.el  = Roo.get(config);
14079         this.tpl = depreciated_tpl;
14080         Roo.apply(this, depreciated_config);
14081     }
14082     this.wrapEl  = this.el.wrap().wrap();
14083     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14084     
14085     
14086     if(typeof(this.tpl) == "string"){
14087         this.tpl = new Roo.Template(this.tpl);
14088     } else {
14089         // support xtype ctors..
14090         this.tpl = new Roo.factory(this.tpl, Roo);
14091     }
14092     
14093     
14094     this.tpl.compile();
14095     
14096     /** @private */
14097     this.addEvents({
14098         /**
14099          * @event beforeclick
14100          * Fires before a click is processed. Returns false to cancel the default action.
14101          * @param {Roo.View} this
14102          * @param {Number} index The index of the target node
14103          * @param {HTMLElement} node The target node
14104          * @param {Roo.EventObject} e The raw event object
14105          */
14106             "beforeclick" : true,
14107         /**
14108          * @event click
14109          * Fires when a template node is clicked.
14110          * @param {Roo.View} this
14111          * @param {Number} index The index of the target node
14112          * @param {HTMLElement} node The target node
14113          * @param {Roo.EventObject} e The raw event object
14114          */
14115             "click" : true,
14116         /**
14117          * @event dblclick
14118          * Fires when a template node is double clicked.
14119          * @param {Roo.View} this
14120          * @param {Number} index The index of the target node
14121          * @param {HTMLElement} node The target node
14122          * @param {Roo.EventObject} e The raw event object
14123          */
14124             "dblclick" : true,
14125         /**
14126          * @event contextmenu
14127          * Fires when a template node is right clicked.
14128          * @param {Roo.View} this
14129          * @param {Number} index The index of the target node
14130          * @param {HTMLElement} node The target node
14131          * @param {Roo.EventObject} e The raw event object
14132          */
14133             "contextmenu" : true,
14134         /**
14135          * @event selectionchange
14136          * Fires when the selected nodes change.
14137          * @param {Roo.View} this
14138          * @param {Array} selections Array of the selected nodes
14139          */
14140             "selectionchange" : true,
14141     
14142         /**
14143          * @event beforeselect
14144          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14145          * @param {Roo.View} this
14146          * @param {HTMLElement} node The node to be selected
14147          * @param {Array} selections Array of currently selected nodes
14148          */
14149             "beforeselect" : true,
14150         /**
14151          * @event preparedata
14152          * Fires on every row to render, to allow you to change the data.
14153          * @param {Roo.View} this
14154          * @param {Object} data to be rendered (change this)
14155          */
14156           "preparedata" : true
14157           
14158           
14159         });
14160
14161
14162
14163     this.el.on({
14164         "click": this.onClick,
14165         "dblclick": this.onDblClick,
14166         "contextmenu": this.onContextMenu,
14167         scope:this
14168     });
14169
14170     this.selections = [];
14171     this.nodes = [];
14172     this.cmp = new Roo.CompositeElementLite([]);
14173     if(this.store){
14174         this.store = Roo.factory(this.store, Roo.data);
14175         this.setStore(this.store, true);
14176     }
14177     
14178     if ( this.footer && this.footer.xtype) {
14179            
14180          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14181         
14182         this.footer.dataSource = this.store;
14183         this.footer.container = fctr;
14184         this.footer = Roo.factory(this.footer, Roo);
14185         fctr.insertFirst(this.el);
14186         
14187         // this is a bit insane - as the paging toolbar seems to detach the el..
14188 //        dom.parentNode.parentNode.parentNode
14189          // they get detached?
14190     }
14191     
14192     
14193     Roo.View.superclass.constructor.call(this);
14194     
14195     
14196 };
14197
14198 Roo.extend(Roo.View, Roo.util.Observable, {
14199     
14200      /**
14201      * @cfg {Roo.data.Store} store Data store to load data from.
14202      */
14203     store : false,
14204     
14205     /**
14206      * @cfg {String|Roo.Element} el The container element.
14207      */
14208     el : '',
14209     
14210     /**
14211      * @cfg {String|Roo.Template} tpl The template used by this View 
14212      */
14213     tpl : false,
14214     /**
14215      * @cfg {String} dataName the named area of the template to use as the data area
14216      *                          Works with domtemplates roo-name="name"
14217      */
14218     dataName: false,
14219     /**
14220      * @cfg {String} selectedClass The css class to add to selected nodes
14221      */
14222     selectedClass : "x-view-selected",
14223      /**
14224      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14225      */
14226     emptyText : "",
14227     
14228     /**
14229      * @cfg {String} text to display on mask (default Loading)
14230      */
14231     mask : false,
14232     /**
14233      * @cfg {Boolean} multiSelect Allow multiple selection
14234      */
14235     multiSelect : false,
14236     /**
14237      * @cfg {Boolean} singleSelect Allow single selection
14238      */
14239     singleSelect:  false,
14240     
14241     /**
14242      * @cfg {Boolean} toggleSelect - selecting 
14243      */
14244     toggleSelect : false,
14245     
14246     /**
14247      * @cfg {Boolean} tickable - selecting 
14248      */
14249     tickable : false,
14250     
14251     /**
14252      * Returns the element this view is bound to.
14253      * @return {Roo.Element}
14254      */
14255     getEl : function(){
14256         return this.wrapEl;
14257     },
14258     
14259     
14260
14261     /**
14262      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14263      */
14264     refresh : function(){
14265         //Roo.log('refresh');
14266         var t = this.tpl;
14267         
14268         // if we are using something like 'domtemplate', then
14269         // the what gets used is:
14270         // t.applySubtemplate(NAME, data, wrapping data..)
14271         // the outer template then get' applied with
14272         //     the store 'extra data'
14273         // and the body get's added to the
14274         //      roo-name="data" node?
14275         //      <span class='roo-tpl-{name}'></span> ?????
14276         
14277         
14278         
14279         this.clearSelections();
14280         this.el.update("");
14281         var html = [];
14282         var records = this.store.getRange();
14283         if(records.length < 1) {
14284             
14285             // is this valid??  = should it render a template??
14286             
14287             this.el.update(this.emptyText);
14288             return;
14289         }
14290         var el = this.el;
14291         if (this.dataName) {
14292             this.el.update(t.apply(this.store.meta)); //????
14293             el = this.el.child('.roo-tpl-' + this.dataName);
14294         }
14295         
14296         for(var i = 0, len = records.length; i < len; i++){
14297             var data = this.prepareData(records[i].data, i, records[i]);
14298             this.fireEvent("preparedata", this, data, i, records[i]);
14299             
14300             var d = Roo.apply({}, data);
14301             
14302             if(this.tickable){
14303                 Roo.apply(d, {'roo-id' : Roo.id()});
14304                 
14305                 var _this = this;
14306             
14307                 Roo.each(this.parent.item, function(item){
14308                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14309                         return;
14310                     }
14311                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14312                 });
14313             }
14314             
14315             html[html.length] = Roo.util.Format.trim(
14316                 this.dataName ?
14317                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14318                     t.apply(d)
14319             );
14320         }
14321         
14322         
14323         
14324         el.update(html.join(""));
14325         this.nodes = el.dom.childNodes;
14326         this.updateIndexes(0);
14327     },
14328     
14329
14330     /**
14331      * Function to override to reformat the data that is sent to
14332      * the template for each node.
14333      * DEPRICATED - use the preparedata event handler.
14334      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14335      * a JSON object for an UpdateManager bound view).
14336      */
14337     prepareData : function(data, index, record)
14338     {
14339         this.fireEvent("preparedata", this, data, index, record);
14340         return data;
14341     },
14342
14343     onUpdate : function(ds, record){
14344         // Roo.log('on update');   
14345         this.clearSelections();
14346         var index = this.store.indexOf(record);
14347         var n = this.nodes[index];
14348         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14349         n.parentNode.removeChild(n);
14350         this.updateIndexes(index, index);
14351     },
14352
14353     
14354     
14355 // --------- FIXME     
14356     onAdd : function(ds, records, index)
14357     {
14358         //Roo.log(['on Add', ds, records, index] );        
14359         this.clearSelections();
14360         if(this.nodes.length == 0){
14361             this.refresh();
14362             return;
14363         }
14364         var n = this.nodes[index];
14365         for(var i = 0, len = records.length; i < len; i++){
14366             var d = this.prepareData(records[i].data, i, records[i]);
14367             if(n){
14368                 this.tpl.insertBefore(n, d);
14369             }else{
14370                 
14371                 this.tpl.append(this.el, d);
14372             }
14373         }
14374         this.updateIndexes(index);
14375     },
14376
14377     onRemove : function(ds, record, index){
14378        // Roo.log('onRemove');
14379         this.clearSelections();
14380         var el = this.dataName  ?
14381             this.el.child('.roo-tpl-' + this.dataName) :
14382             this.el; 
14383         
14384         el.dom.removeChild(this.nodes[index]);
14385         this.updateIndexes(index);
14386     },
14387
14388     /**
14389      * Refresh an individual node.
14390      * @param {Number} index
14391      */
14392     refreshNode : function(index){
14393         this.onUpdate(this.store, this.store.getAt(index));
14394     },
14395
14396     updateIndexes : function(startIndex, endIndex){
14397         var ns = this.nodes;
14398         startIndex = startIndex || 0;
14399         endIndex = endIndex || ns.length - 1;
14400         for(var i = startIndex; i <= endIndex; i++){
14401             ns[i].nodeIndex = i;
14402         }
14403     },
14404
14405     /**
14406      * Changes the data store this view uses and refresh the view.
14407      * @param {Store} store
14408      */
14409     setStore : function(store, initial){
14410         if(!initial && this.store){
14411             this.store.un("datachanged", this.refresh);
14412             this.store.un("add", this.onAdd);
14413             this.store.un("remove", this.onRemove);
14414             this.store.un("update", this.onUpdate);
14415             this.store.un("clear", this.refresh);
14416             this.store.un("beforeload", this.onBeforeLoad);
14417             this.store.un("load", this.onLoad);
14418             this.store.un("loadexception", this.onLoad);
14419         }
14420         if(store){
14421           
14422             store.on("datachanged", this.refresh, this);
14423             store.on("add", this.onAdd, this);
14424             store.on("remove", this.onRemove, this);
14425             store.on("update", this.onUpdate, this);
14426             store.on("clear", this.refresh, this);
14427             store.on("beforeload", this.onBeforeLoad, this);
14428             store.on("load", this.onLoad, this);
14429             store.on("loadexception", this.onLoad, this);
14430         }
14431         
14432         if(store){
14433             this.refresh();
14434         }
14435     },
14436     /**
14437      * onbeforeLoad - masks the loading area.
14438      *
14439      */
14440     onBeforeLoad : function(store,opts)
14441     {
14442          //Roo.log('onBeforeLoad');   
14443         if (!opts.add) {
14444             this.el.update("");
14445         }
14446         this.el.mask(this.mask ? this.mask : "Loading" ); 
14447     },
14448     onLoad : function ()
14449     {
14450         this.el.unmask();
14451     },
14452     
14453
14454     /**
14455      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14456      * @param {HTMLElement} node
14457      * @return {HTMLElement} The template node
14458      */
14459     findItemFromChild : function(node){
14460         var el = this.dataName  ?
14461             this.el.child('.roo-tpl-' + this.dataName,true) :
14462             this.el.dom; 
14463         
14464         if(!node || node.parentNode == el){
14465                     return node;
14466             }
14467             var p = node.parentNode;
14468             while(p && p != el){
14469             if(p.parentNode == el){
14470                 return p;
14471             }
14472             p = p.parentNode;
14473         }
14474             return null;
14475     },
14476
14477     /** @ignore */
14478     onClick : function(e){
14479         var item = this.findItemFromChild(e.getTarget());
14480         if(item){
14481             var index = this.indexOf(item);
14482             if(this.onItemClick(item, index, e) !== false){
14483                 this.fireEvent("click", this, index, item, e);
14484             }
14485         }else{
14486             this.clearSelections();
14487         }
14488     },
14489
14490     /** @ignore */
14491     onContextMenu : function(e){
14492         var item = this.findItemFromChild(e.getTarget());
14493         if(item){
14494             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14495         }
14496     },
14497
14498     /** @ignore */
14499     onDblClick : function(e){
14500         var item = this.findItemFromChild(e.getTarget());
14501         if(item){
14502             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14503         }
14504     },
14505
14506     onItemClick : function(item, index, e)
14507     {
14508         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14509             return false;
14510         }
14511         if (this.toggleSelect) {
14512             var m = this.isSelected(item) ? 'unselect' : 'select';
14513             //Roo.log(m);
14514             var _t = this;
14515             _t[m](item, true, false);
14516             return true;
14517         }
14518         if(this.multiSelect || this.singleSelect){
14519             if(this.multiSelect && e.shiftKey && this.lastSelection){
14520                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14521             }else{
14522                 this.select(item, this.multiSelect && e.ctrlKey);
14523                 this.lastSelection = item;
14524             }
14525             
14526             if(!this.tickable){
14527                 e.preventDefault();
14528             }
14529             
14530         }
14531         return true;
14532     },
14533
14534     /**
14535      * Get the number of selected nodes.
14536      * @return {Number}
14537      */
14538     getSelectionCount : function(){
14539         return this.selections.length;
14540     },
14541
14542     /**
14543      * Get the currently selected nodes.
14544      * @return {Array} An array of HTMLElements
14545      */
14546     getSelectedNodes : function(){
14547         return this.selections;
14548     },
14549
14550     /**
14551      * Get the indexes of the selected nodes.
14552      * @return {Array}
14553      */
14554     getSelectedIndexes : function(){
14555         var indexes = [], s = this.selections;
14556         for(var i = 0, len = s.length; i < len; i++){
14557             indexes.push(s[i].nodeIndex);
14558         }
14559         return indexes;
14560     },
14561
14562     /**
14563      * Clear all selections
14564      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14565      */
14566     clearSelections : function(suppressEvent){
14567         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14568             this.cmp.elements = this.selections;
14569             this.cmp.removeClass(this.selectedClass);
14570             this.selections = [];
14571             if(!suppressEvent){
14572                 this.fireEvent("selectionchange", this, this.selections);
14573             }
14574         }
14575     },
14576
14577     /**
14578      * Returns true if the passed node is selected
14579      * @param {HTMLElement/Number} node The node or node index
14580      * @return {Boolean}
14581      */
14582     isSelected : function(node){
14583         var s = this.selections;
14584         if(s.length < 1){
14585             return false;
14586         }
14587         node = this.getNode(node);
14588         return s.indexOf(node) !== -1;
14589     },
14590
14591     /**
14592      * Selects nodes.
14593      * @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
14594      * @param {Boolean} keepExisting (optional) true to keep existing selections
14595      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14596      */
14597     select : function(nodeInfo, keepExisting, suppressEvent){
14598         if(nodeInfo instanceof Array){
14599             if(!keepExisting){
14600                 this.clearSelections(true);
14601             }
14602             for(var i = 0, len = nodeInfo.length; i < len; i++){
14603                 this.select(nodeInfo[i], true, true);
14604             }
14605             return;
14606         } 
14607         var node = this.getNode(nodeInfo);
14608         if(!node || this.isSelected(node)){
14609             return; // already selected.
14610         }
14611         if(!keepExisting){
14612             this.clearSelections(true);
14613         }
14614         
14615         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14616             Roo.fly(node).addClass(this.selectedClass);
14617             this.selections.push(node);
14618             if(!suppressEvent){
14619                 this.fireEvent("selectionchange", this, this.selections);
14620             }
14621         }
14622         
14623         
14624     },
14625       /**
14626      * Unselects nodes.
14627      * @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
14628      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14629      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14630      */
14631     unselect : function(nodeInfo, keepExisting, suppressEvent)
14632     {
14633         if(nodeInfo instanceof Array){
14634             Roo.each(this.selections, function(s) {
14635                 this.unselect(s, nodeInfo);
14636             }, this);
14637             return;
14638         }
14639         var node = this.getNode(nodeInfo);
14640         if(!node || !this.isSelected(node)){
14641             //Roo.log("not selected");
14642             return; // not selected.
14643         }
14644         // fireevent???
14645         var ns = [];
14646         Roo.each(this.selections, function(s) {
14647             if (s == node ) {
14648                 Roo.fly(node).removeClass(this.selectedClass);
14649
14650                 return;
14651             }
14652             ns.push(s);
14653         },this);
14654         
14655         this.selections= ns;
14656         this.fireEvent("selectionchange", this, this.selections);
14657     },
14658
14659     /**
14660      * Gets a template node.
14661      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14662      * @return {HTMLElement} The node or null if it wasn't found
14663      */
14664     getNode : function(nodeInfo){
14665         if(typeof nodeInfo == "string"){
14666             return document.getElementById(nodeInfo);
14667         }else if(typeof nodeInfo == "number"){
14668             return this.nodes[nodeInfo];
14669         }
14670         return nodeInfo;
14671     },
14672
14673     /**
14674      * Gets a range template nodes.
14675      * @param {Number} startIndex
14676      * @param {Number} endIndex
14677      * @return {Array} An array of nodes
14678      */
14679     getNodes : function(start, end){
14680         var ns = this.nodes;
14681         start = start || 0;
14682         end = typeof end == "undefined" ? ns.length - 1 : end;
14683         var nodes = [];
14684         if(start <= end){
14685             for(var i = start; i <= end; i++){
14686                 nodes.push(ns[i]);
14687             }
14688         } else{
14689             for(var i = start; i >= end; i--){
14690                 nodes.push(ns[i]);
14691             }
14692         }
14693         return nodes;
14694     },
14695
14696     /**
14697      * Finds the index of the passed node
14698      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14699      * @return {Number} The index of the node or -1
14700      */
14701     indexOf : function(node){
14702         node = this.getNode(node);
14703         if(typeof node.nodeIndex == "number"){
14704             return node.nodeIndex;
14705         }
14706         var ns = this.nodes;
14707         for(var i = 0, len = ns.length; i < len; i++){
14708             if(ns[i] == node){
14709                 return i;
14710             }
14711         }
14712         return -1;
14713     }
14714 });
14715 /*
14716  * - LGPL
14717  *
14718  * based on jquery fullcalendar
14719  * 
14720  */
14721
14722 Roo.bootstrap = Roo.bootstrap || {};
14723 /**
14724  * @class Roo.bootstrap.Calendar
14725  * @extends Roo.bootstrap.Component
14726  * Bootstrap Calendar class
14727  * @cfg {Boolean} loadMask (true|false) default false
14728  * @cfg {Object} header generate the user specific header of the calendar, default false
14729
14730  * @constructor
14731  * Create a new Container
14732  * @param {Object} config The config object
14733  */
14734
14735
14736
14737 Roo.bootstrap.Calendar = function(config){
14738     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14739      this.addEvents({
14740         /**
14741              * @event select
14742              * Fires when a date is selected
14743              * @param {DatePicker} this
14744              * @param {Date} date The selected date
14745              */
14746         'select': true,
14747         /**
14748              * @event monthchange
14749              * Fires when the displayed month changes 
14750              * @param {DatePicker} this
14751              * @param {Date} date The selected month
14752              */
14753         'monthchange': true,
14754         /**
14755              * @event evententer
14756              * Fires when mouse over an event
14757              * @param {Calendar} this
14758              * @param {event} Event
14759              */
14760         'evententer': true,
14761         /**
14762              * @event eventleave
14763              * Fires when the mouse leaves an
14764              * @param {Calendar} this
14765              * @param {event}
14766              */
14767         'eventleave': true,
14768         /**
14769              * @event eventclick
14770              * Fires when the mouse click an
14771              * @param {Calendar} this
14772              * @param {event}
14773              */
14774         'eventclick': true
14775         
14776     });
14777
14778 };
14779
14780 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14781     
14782      /**
14783      * @cfg {Number} startDay
14784      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14785      */
14786     startDay : 0,
14787     
14788     loadMask : false,
14789     
14790     header : false,
14791       
14792     getAutoCreate : function(){
14793         
14794         
14795         var fc_button = function(name, corner, style, content ) {
14796             return Roo.apply({},{
14797                 tag : 'span',
14798                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14799                          (corner.length ?
14800                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14801                             ''
14802                         ),
14803                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14804                 unselectable: 'on'
14805             });
14806         };
14807         
14808         var header = {};
14809         
14810         if(!this.header){
14811             header = {
14812                 tag : 'table',
14813                 cls : 'fc-header',
14814                 style : 'width:100%',
14815                 cn : [
14816                     {
14817                         tag: 'tr',
14818                         cn : [
14819                             {
14820                                 tag : 'td',
14821                                 cls : 'fc-header-left',
14822                                 cn : [
14823                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14824                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14825                                     { tag: 'span', cls: 'fc-header-space' },
14826                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14827
14828
14829                                 ]
14830                             },
14831
14832                             {
14833                                 tag : 'td',
14834                                 cls : 'fc-header-center',
14835                                 cn : [
14836                                     {
14837                                         tag: 'span',
14838                                         cls: 'fc-header-title',
14839                                         cn : {
14840                                             tag: 'H2',
14841                                             html : 'month / year'
14842                                         }
14843                                     }
14844
14845                                 ]
14846                             },
14847                             {
14848                                 tag : 'td',
14849                                 cls : 'fc-header-right',
14850                                 cn : [
14851                               /*      fc_button('month', 'left', '', 'month' ),
14852                                     fc_button('week', '', '', 'week' ),
14853                                     fc_button('day', 'right', '', 'day' )
14854                                 */    
14855
14856                                 ]
14857                             }
14858
14859                         ]
14860                     }
14861                 ]
14862             };
14863         }
14864         
14865         header = this.header;
14866         
14867        
14868         var cal_heads = function() {
14869             var ret = [];
14870             // fixme - handle this.
14871             
14872             for (var i =0; i < Date.dayNames.length; i++) {
14873                 var d = Date.dayNames[i];
14874                 ret.push({
14875                     tag: 'th',
14876                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14877                     html : d.substring(0,3)
14878                 });
14879                 
14880             }
14881             ret[0].cls += ' fc-first';
14882             ret[6].cls += ' fc-last';
14883             return ret;
14884         };
14885         var cal_cell = function(n) {
14886             return  {
14887                 tag: 'td',
14888                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14889                 cn : [
14890                     {
14891                         cn : [
14892                             {
14893                                 cls: 'fc-day-number',
14894                                 html: 'D'
14895                             },
14896                             {
14897                                 cls: 'fc-day-content',
14898                              
14899                                 cn : [
14900                                      {
14901                                         style: 'position: relative;' // height: 17px;
14902                                     }
14903                                 ]
14904                             }
14905                             
14906                             
14907                         ]
14908                     }
14909                 ]
14910                 
14911             }
14912         };
14913         var cal_rows = function() {
14914             
14915             var ret = [];
14916             for (var r = 0; r < 6; r++) {
14917                 var row= {
14918                     tag : 'tr',
14919                     cls : 'fc-week',
14920                     cn : []
14921                 };
14922                 
14923                 for (var i =0; i < Date.dayNames.length; i++) {
14924                     var d = Date.dayNames[i];
14925                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
14926
14927                 }
14928                 row.cn[0].cls+=' fc-first';
14929                 row.cn[0].cn[0].style = 'min-height:90px';
14930                 row.cn[6].cls+=' fc-last';
14931                 ret.push(row);
14932                 
14933             }
14934             ret[0].cls += ' fc-first';
14935             ret[4].cls += ' fc-prev-last';
14936             ret[5].cls += ' fc-last';
14937             return ret;
14938             
14939         };
14940         
14941         var cal_table = {
14942             tag: 'table',
14943             cls: 'fc-border-separate',
14944             style : 'width:100%',
14945             cellspacing  : 0,
14946             cn : [
14947                 { 
14948                     tag: 'thead',
14949                     cn : [
14950                         { 
14951                             tag: 'tr',
14952                             cls : 'fc-first fc-last',
14953                             cn : cal_heads()
14954                         }
14955                     ]
14956                 },
14957                 { 
14958                     tag: 'tbody',
14959                     cn : cal_rows()
14960                 }
14961                   
14962             ]
14963         };
14964          
14965          var cfg = {
14966             cls : 'fc fc-ltr',
14967             cn : [
14968                 header,
14969                 {
14970                     cls : 'fc-content',
14971                     style : "position: relative;",
14972                     cn : [
14973                         {
14974                             cls : 'fc-view fc-view-month fc-grid',
14975                             style : 'position: relative',
14976                             unselectable : 'on',
14977                             cn : [
14978                                 {
14979                                     cls : 'fc-event-container',
14980                                     style : 'position:absolute;z-index:8;top:0;left:0;'
14981                                 },
14982                                 cal_table
14983                             ]
14984                         }
14985                     ]
14986     
14987                 }
14988            ] 
14989             
14990         };
14991         
14992          
14993         
14994         return cfg;
14995     },
14996     
14997     
14998     initEvents : function()
14999     {
15000         if(!this.store){
15001             throw "can not find store for calendar";
15002         }
15003         
15004         var mark = {
15005             tag: "div",
15006             cls:"x-dlg-mask",
15007             style: "text-align:center",
15008             cn: [
15009                 {
15010                     tag: "div",
15011                     style: "background-color:white;width:50%;margin:250 auto",
15012                     cn: [
15013                         {
15014                             tag: "img",
15015                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15016                         },
15017                         {
15018                             tag: "span",
15019                             html: "Loading"
15020                         }
15021                         
15022                     ]
15023                 }
15024             ]
15025         };
15026         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15027         
15028         var size = this.el.select('.fc-content', true).first().getSize();
15029         this.maskEl.setSize(size.width, size.height);
15030         this.maskEl.enableDisplayMode("block");
15031         if(!this.loadMask){
15032             this.maskEl.hide();
15033         }
15034         
15035         this.store = Roo.factory(this.store, Roo.data);
15036         this.store.on('load', this.onLoad, this);
15037         this.store.on('beforeload', this.onBeforeLoad, this);
15038         
15039         this.resize();
15040         
15041         this.cells = this.el.select('.fc-day',true);
15042         //Roo.log(this.cells);
15043         this.textNodes = this.el.query('.fc-day-number');
15044         this.cells.addClassOnOver('fc-state-hover');
15045         
15046         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15047         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15048         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15049         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15050         
15051         this.on('monthchange', this.onMonthChange, this);
15052         
15053         this.update(new Date().clearTime());
15054     },
15055     
15056     resize : function() {
15057         var sz  = this.el.getSize();
15058         
15059         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15060         this.el.select('.fc-day-content div',true).setHeight(34);
15061     },
15062     
15063     
15064     // private
15065     showPrevMonth : function(e){
15066         this.update(this.activeDate.add("mo", -1));
15067     },
15068     showToday : function(e){
15069         this.update(new Date().clearTime());
15070     },
15071     // private
15072     showNextMonth : function(e){
15073         this.update(this.activeDate.add("mo", 1));
15074     },
15075
15076     // private
15077     showPrevYear : function(){
15078         this.update(this.activeDate.add("y", -1));
15079     },
15080
15081     // private
15082     showNextYear : function(){
15083         this.update(this.activeDate.add("y", 1));
15084     },
15085
15086     
15087    // private
15088     update : function(date)
15089     {
15090         var vd = this.activeDate;
15091         this.activeDate = date;
15092 //        if(vd && this.el){
15093 //            var t = date.getTime();
15094 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15095 //                Roo.log('using add remove');
15096 //                
15097 //                this.fireEvent('monthchange', this, date);
15098 //                
15099 //                this.cells.removeClass("fc-state-highlight");
15100 //                this.cells.each(function(c){
15101 //                   if(c.dateValue == t){
15102 //                       c.addClass("fc-state-highlight");
15103 //                       setTimeout(function(){
15104 //                            try{c.dom.firstChild.focus();}catch(e){}
15105 //                       }, 50);
15106 //                       return false;
15107 //                   }
15108 //                   return true;
15109 //                });
15110 //                return;
15111 //            }
15112 //        }
15113         
15114         var days = date.getDaysInMonth();
15115         
15116         var firstOfMonth = date.getFirstDateOfMonth();
15117         var startingPos = firstOfMonth.getDay()-this.startDay;
15118         
15119         if(startingPos < this.startDay){
15120             startingPos += 7;
15121         }
15122         
15123         var pm = date.add(Date.MONTH, -1);
15124         var prevStart = pm.getDaysInMonth()-startingPos;
15125 //        
15126         this.cells = this.el.select('.fc-day',true);
15127         this.textNodes = this.el.query('.fc-day-number');
15128         this.cells.addClassOnOver('fc-state-hover');
15129         
15130         var cells = this.cells.elements;
15131         var textEls = this.textNodes;
15132         
15133         Roo.each(cells, function(cell){
15134             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15135         });
15136         
15137         days += startingPos;
15138
15139         // convert everything to numbers so it's fast
15140         var day = 86400000;
15141         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15142         //Roo.log(d);
15143         //Roo.log(pm);
15144         //Roo.log(prevStart);
15145         
15146         var today = new Date().clearTime().getTime();
15147         var sel = date.clearTime().getTime();
15148         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15149         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15150         var ddMatch = this.disabledDatesRE;
15151         var ddText = this.disabledDatesText;
15152         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15153         var ddaysText = this.disabledDaysText;
15154         var format = this.format;
15155         
15156         var setCellClass = function(cal, cell){
15157             cell.row = 0;
15158             cell.events = [];
15159             cell.more = [];
15160             //Roo.log('set Cell Class');
15161             cell.title = "";
15162             var t = d.getTime();
15163             
15164             //Roo.log(d);
15165             
15166             cell.dateValue = t;
15167             if(t == today){
15168                 cell.className += " fc-today";
15169                 cell.className += " fc-state-highlight";
15170                 cell.title = cal.todayText;
15171             }
15172             if(t == sel){
15173                 // disable highlight in other month..
15174                 //cell.className += " fc-state-highlight";
15175                 
15176             }
15177             // disabling
15178             if(t < min) {
15179                 cell.className = " fc-state-disabled";
15180                 cell.title = cal.minText;
15181                 return;
15182             }
15183             if(t > max) {
15184                 cell.className = " fc-state-disabled";
15185                 cell.title = cal.maxText;
15186                 return;
15187             }
15188             if(ddays){
15189                 if(ddays.indexOf(d.getDay()) != -1){
15190                     cell.title = ddaysText;
15191                     cell.className = " fc-state-disabled";
15192                 }
15193             }
15194             if(ddMatch && format){
15195                 var fvalue = d.dateFormat(format);
15196                 if(ddMatch.test(fvalue)){
15197                     cell.title = ddText.replace("%0", fvalue);
15198                     cell.className = " fc-state-disabled";
15199                 }
15200             }
15201             
15202             if (!cell.initialClassName) {
15203                 cell.initialClassName = cell.dom.className;
15204             }
15205             
15206             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15207         };
15208
15209         var i = 0;
15210         
15211         for(; i < startingPos; i++) {
15212             textEls[i].innerHTML = (++prevStart);
15213             d.setDate(d.getDate()+1);
15214             
15215             cells[i].className = "fc-past fc-other-month";
15216             setCellClass(this, cells[i]);
15217         }
15218         
15219         var intDay = 0;
15220         
15221         for(; i < days; i++){
15222             intDay = i - startingPos + 1;
15223             textEls[i].innerHTML = (intDay);
15224             d.setDate(d.getDate()+1);
15225             
15226             cells[i].className = ''; // "x-date-active";
15227             setCellClass(this, cells[i]);
15228         }
15229         var extraDays = 0;
15230         
15231         for(; i < 42; i++) {
15232             textEls[i].innerHTML = (++extraDays);
15233             d.setDate(d.getDate()+1);
15234             
15235             cells[i].className = "fc-future fc-other-month";
15236             setCellClass(this, cells[i]);
15237         }
15238         
15239         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15240         
15241         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15242         
15243         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15244         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15245         
15246         if(totalRows != 6){
15247             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15248             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15249         }
15250         
15251         this.fireEvent('monthchange', this, date);
15252         
15253         
15254         /*
15255         if(!this.internalRender){
15256             var main = this.el.dom.firstChild;
15257             var w = main.offsetWidth;
15258             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15259             Roo.fly(main).setWidth(w);
15260             this.internalRender = true;
15261             // opera does not respect the auto grow header center column
15262             // then, after it gets a width opera refuses to recalculate
15263             // without a second pass
15264             if(Roo.isOpera && !this.secondPass){
15265                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15266                 this.secondPass = true;
15267                 this.update.defer(10, this, [date]);
15268             }
15269         }
15270         */
15271         
15272     },
15273     
15274     findCell : function(dt) {
15275         dt = dt.clearTime().getTime();
15276         var ret = false;
15277         this.cells.each(function(c){
15278             //Roo.log("check " +c.dateValue + '?=' + dt);
15279             if(c.dateValue == dt){
15280                 ret = c;
15281                 return false;
15282             }
15283             return true;
15284         });
15285         
15286         return ret;
15287     },
15288     
15289     findCells : function(ev) {
15290         var s = ev.start.clone().clearTime().getTime();
15291        // Roo.log(s);
15292         var e= ev.end.clone().clearTime().getTime();
15293        // Roo.log(e);
15294         var ret = [];
15295         this.cells.each(function(c){
15296              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15297             
15298             if(c.dateValue > e){
15299                 return ;
15300             }
15301             if(c.dateValue < s){
15302                 return ;
15303             }
15304             ret.push(c);
15305         });
15306         
15307         return ret;    
15308     },
15309     
15310 //    findBestRow: function(cells)
15311 //    {
15312 //        var ret = 0;
15313 //        
15314 //        for (var i =0 ; i < cells.length;i++) {
15315 //            ret  = Math.max(cells[i].rows || 0,ret);
15316 //        }
15317 //        return ret;
15318 //        
15319 //    },
15320     
15321     
15322     addItem : function(ev)
15323     {
15324         // look for vertical location slot in
15325         var cells = this.findCells(ev);
15326         
15327 //        ev.row = this.findBestRow(cells);
15328         
15329         // work out the location.
15330         
15331         var crow = false;
15332         var rows = [];
15333         for(var i =0; i < cells.length; i++) {
15334             
15335             cells[i].row = cells[0].row;
15336             
15337             if(i == 0){
15338                 cells[i].row = cells[i].row + 1;
15339             }
15340             
15341             if (!crow) {
15342                 crow = {
15343                     start : cells[i],
15344                     end :  cells[i]
15345                 };
15346                 continue;
15347             }
15348             if (crow.start.getY() == cells[i].getY()) {
15349                 // on same row.
15350                 crow.end = cells[i];
15351                 continue;
15352             }
15353             // different row.
15354             rows.push(crow);
15355             crow = {
15356                 start: cells[i],
15357                 end : cells[i]
15358             };
15359             
15360         }
15361         
15362         rows.push(crow);
15363         ev.els = [];
15364         ev.rows = rows;
15365         ev.cells = cells;
15366         
15367         cells[0].events.push(ev);
15368         
15369         this.calevents.push(ev);
15370     },
15371     
15372     clearEvents: function() {
15373         
15374         if(!this.calevents){
15375             return;
15376         }
15377         
15378         Roo.each(this.cells.elements, function(c){
15379             c.row = 0;
15380             c.events = [];
15381             c.more = [];
15382         });
15383         
15384         Roo.each(this.calevents, function(e) {
15385             Roo.each(e.els, function(el) {
15386                 el.un('mouseenter' ,this.onEventEnter, this);
15387                 el.un('mouseleave' ,this.onEventLeave, this);
15388                 el.remove();
15389             },this);
15390         },this);
15391         
15392         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15393             e.remove();
15394         });
15395         
15396     },
15397     
15398     renderEvents: function()
15399     {   
15400         var _this = this;
15401         
15402         this.cells.each(function(c) {
15403             
15404             if(c.row < 5){
15405                 return;
15406             }
15407             
15408             var ev = c.events;
15409             
15410             var r = 4;
15411             if(c.row != c.events.length){
15412                 r = 4 - (4 - (c.row - c.events.length));
15413             }
15414             
15415             c.events = ev.slice(0, r);
15416             c.more = ev.slice(r);
15417             
15418             if(c.more.length && c.more.length == 1){
15419                 c.events.push(c.more.pop());
15420             }
15421             
15422             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15423             
15424         });
15425             
15426         this.cells.each(function(c) {
15427             
15428             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15429             
15430             
15431             for (var e = 0; e < c.events.length; e++){
15432                 var ev = c.events[e];
15433                 var rows = ev.rows;
15434                 
15435                 for(var i = 0; i < rows.length; i++) {
15436                 
15437                     // how many rows should it span..
15438
15439                     var  cfg = {
15440                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15441                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15442
15443                         unselectable : "on",
15444                         cn : [
15445                             {
15446                                 cls: 'fc-event-inner',
15447                                 cn : [
15448     //                                {
15449     //                                  tag:'span',
15450     //                                  cls: 'fc-event-time',
15451     //                                  html : cells.length > 1 ? '' : ev.time
15452     //                                },
15453                                     {
15454                                       tag:'span',
15455                                       cls: 'fc-event-title',
15456                                       html : String.format('{0}', ev.title)
15457                                     }
15458
15459
15460                                 ]
15461                             },
15462                             {
15463                                 cls: 'ui-resizable-handle ui-resizable-e',
15464                                 html : '&nbsp;&nbsp;&nbsp'
15465                             }
15466
15467                         ]
15468                     };
15469
15470                     if (i == 0) {
15471                         cfg.cls += ' fc-event-start';
15472                     }
15473                     if ((i+1) == rows.length) {
15474                         cfg.cls += ' fc-event-end';
15475                     }
15476
15477                     var ctr = _this.el.select('.fc-event-container',true).first();
15478                     var cg = ctr.createChild(cfg);
15479
15480                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15481                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15482
15483                     var r = (c.more.length) ? 1 : 0;
15484                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15485                     cg.setWidth(ebox.right - sbox.x -2);
15486
15487                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15488                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15489                     cg.on('click', _this.onEventClick, _this, ev);
15490
15491                     ev.els.push(cg);
15492                     
15493                 }
15494                 
15495             }
15496             
15497             
15498             if(c.more.length){
15499                 var  cfg = {
15500                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15501                     style : 'position: absolute',
15502                     unselectable : "on",
15503                     cn : [
15504                         {
15505                             cls: 'fc-event-inner',
15506                             cn : [
15507                                 {
15508                                   tag:'span',
15509                                   cls: 'fc-event-title',
15510                                   html : 'More'
15511                                 }
15512
15513
15514                             ]
15515                         },
15516                         {
15517                             cls: 'ui-resizable-handle ui-resizable-e',
15518                             html : '&nbsp;&nbsp;&nbsp'
15519                         }
15520
15521                     ]
15522                 };
15523
15524                 var ctr = _this.el.select('.fc-event-container',true).first();
15525                 var cg = ctr.createChild(cfg);
15526
15527                 var sbox = c.select('.fc-day-content',true).first().getBox();
15528                 var ebox = c.select('.fc-day-content',true).first().getBox();
15529                 //Roo.log(cg);
15530                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15531                 cg.setWidth(ebox.right - sbox.x -2);
15532
15533                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15534                 
15535             }
15536             
15537         });
15538         
15539         
15540         
15541     },
15542     
15543     onEventEnter: function (e, el,event,d) {
15544         this.fireEvent('evententer', this, el, event);
15545     },
15546     
15547     onEventLeave: function (e, el,event,d) {
15548         this.fireEvent('eventleave', this, el, event);
15549     },
15550     
15551     onEventClick: function (e, el,event,d) {
15552         this.fireEvent('eventclick', this, el, event);
15553     },
15554     
15555     onMonthChange: function () {
15556         this.store.load();
15557     },
15558     
15559     onMoreEventClick: function(e, el, more)
15560     {
15561         var _this = this;
15562         
15563         this.calpopover.placement = 'right';
15564         this.calpopover.setTitle('More');
15565         
15566         this.calpopover.setContent('');
15567         
15568         var ctr = this.calpopover.el.select('.popover-content', true).first();
15569         
15570         Roo.each(more, function(m){
15571             var cfg = {
15572                 cls : 'fc-event-hori fc-event-draggable',
15573                 html : m.title
15574             };
15575             var cg = ctr.createChild(cfg);
15576             
15577             cg.on('click', _this.onEventClick, _this, m);
15578         });
15579         
15580         this.calpopover.show(el);
15581         
15582         
15583     },
15584     
15585     onLoad: function () 
15586     {   
15587         this.calevents = [];
15588         var cal = this;
15589         
15590         if(this.store.getCount() > 0){
15591             this.store.data.each(function(d){
15592                cal.addItem({
15593                     id : d.data.id,
15594                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15595                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15596                     time : d.data.start_time,
15597                     title : d.data.title,
15598                     description : d.data.description,
15599                     venue : d.data.venue
15600                 });
15601             });
15602         }
15603         
15604         this.renderEvents();
15605         
15606         if(this.calevents.length && this.loadMask){
15607             this.maskEl.hide();
15608         }
15609     },
15610     
15611     onBeforeLoad: function()
15612     {
15613         this.clearEvents();
15614         if(this.loadMask){
15615             this.maskEl.show();
15616         }
15617     }
15618 });
15619
15620  
15621  /*
15622  * - LGPL
15623  *
15624  * element
15625  * 
15626  */
15627
15628 /**
15629  * @class Roo.bootstrap.Popover
15630  * @extends Roo.bootstrap.Component
15631  * Bootstrap Popover class
15632  * @cfg {String} html contents of the popover   (or false to use children..)
15633  * @cfg {String} title of popover (or false to hide)
15634  * @cfg {String} placement how it is placed
15635  * @cfg {String} trigger click || hover (or false to trigger manually)
15636  * @cfg {String} over what (parent or false to trigger manually.)
15637  * @cfg {Number} delay - delay before showing
15638  
15639  * @constructor
15640  * Create a new Popover
15641  * @param {Object} config The config object
15642  */
15643
15644 Roo.bootstrap.Popover = function(config){
15645     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15646     
15647     this.addEvents({
15648         // raw events
15649          /**
15650          * @event show
15651          * After the popover show
15652          * 
15653          * @param {Roo.bootstrap.Popover} this
15654          */
15655         "show" : true,
15656         /**
15657          * @event hide
15658          * After the popover hide
15659          * 
15660          * @param {Roo.bootstrap.Popover} this
15661          */
15662         "hide" : true
15663     });
15664 };
15665
15666 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15667     
15668     title: 'Fill in a title',
15669     html: false,
15670     
15671     placement : 'right',
15672     trigger : 'hover', // hover
15673     
15674     delay : 0,
15675     
15676     over: 'parent',
15677     
15678     can_build_overlaid : false,
15679     
15680     getChildContainer : function()
15681     {
15682         return this.el.select('.popover-content',true).first();
15683     },
15684     
15685     getAutoCreate : function(){
15686          
15687         var cfg = {
15688            cls : 'popover roo-dynamic',
15689            style: 'display:block',
15690            cn : [
15691                 {
15692                     cls : 'arrow'
15693                 },
15694                 {
15695                     cls : 'popover-inner',
15696                     cn : [
15697                         {
15698                             tag: 'h3',
15699                             cls: 'popover-title',
15700                             html : this.title
15701                         },
15702                         {
15703                             cls : 'popover-content',
15704                             html : this.html
15705                         }
15706                     ]
15707                     
15708                 }
15709            ]
15710         };
15711         
15712         return cfg;
15713     },
15714     setTitle: function(str)
15715     {
15716         this.title = str;
15717         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15718     },
15719     setContent: function(str)
15720     {
15721         this.html = str;
15722         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15723     },
15724     // as it get's added to the bottom of the page.
15725     onRender : function(ct, position)
15726     {
15727         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15728         if(!this.el){
15729             var cfg = Roo.apply({},  this.getAutoCreate());
15730             cfg.id = Roo.id();
15731             
15732             if (this.cls) {
15733                 cfg.cls += ' ' + this.cls;
15734             }
15735             if (this.style) {
15736                 cfg.style = this.style;
15737             }
15738             //Roo.log("adding to ");
15739             this.el = Roo.get(document.body).createChild(cfg, position);
15740 //            Roo.log(this.el);
15741         }
15742         this.initEvents();
15743     },
15744     
15745     initEvents : function()
15746     {
15747         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15748         this.el.enableDisplayMode('block');
15749         this.el.hide();
15750         if (this.over === false) {
15751             return; 
15752         }
15753         if (this.triggers === false) {
15754             return;
15755         }
15756         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15757         var triggers = this.trigger ? this.trigger.split(' ') : [];
15758         Roo.each(triggers, function(trigger) {
15759         
15760             if (trigger == 'click') {
15761                 on_el.on('click', this.toggle, this);
15762             } else if (trigger != 'manual') {
15763                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15764                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15765       
15766                 on_el.on(eventIn  ,this.enter, this);
15767                 on_el.on(eventOut, this.leave, this);
15768             }
15769         }, this);
15770         
15771     },
15772     
15773     
15774     // private
15775     timeout : null,
15776     hoverState : null,
15777     
15778     toggle : function () {
15779         this.hoverState == 'in' ? this.leave() : this.enter();
15780     },
15781     
15782     enter : function () {
15783        
15784     
15785         clearTimeout(this.timeout);
15786     
15787         this.hoverState = 'in';
15788     
15789         if (!this.delay || !this.delay.show) {
15790             this.show();
15791             return;
15792         }
15793         var _t = this;
15794         this.timeout = setTimeout(function () {
15795             if (_t.hoverState == 'in') {
15796                 _t.show();
15797             }
15798         }, this.delay.show)
15799     },
15800     leave : function() {
15801         clearTimeout(this.timeout);
15802     
15803         this.hoverState = 'out';
15804     
15805         if (!this.delay || !this.delay.hide) {
15806             this.hide();
15807             return;
15808         }
15809         var _t = this;
15810         this.timeout = setTimeout(function () {
15811             if (_t.hoverState == 'out') {
15812                 _t.hide();
15813             }
15814         }, this.delay.hide)
15815     },
15816     
15817     show : function (on_el)
15818     {
15819         if (!on_el) {
15820             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15821         }
15822         // set content.
15823         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15824         if (this.html !== false) {
15825             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15826         }
15827         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15828         if (!this.title.length) {
15829             this.el.select('.popover-title',true).hide();
15830         }
15831         
15832         var placement = typeof this.placement == 'function' ?
15833             this.placement.call(this, this.el, on_el) :
15834             this.placement;
15835             
15836         var autoToken = /\s?auto?\s?/i;
15837         var autoPlace = autoToken.test(placement);
15838         if (autoPlace) {
15839             placement = placement.replace(autoToken, '') || 'top';
15840         }
15841         
15842         //this.el.detach()
15843         //this.el.setXY([0,0]);
15844         this.el.show();
15845         this.el.dom.style.display='block';
15846         this.el.addClass(placement);
15847         
15848         //this.el.appendTo(on_el);
15849         
15850         var p = this.getPosition();
15851         var box = this.el.getBox();
15852         
15853         if (autoPlace) {
15854             // fixme..
15855         }
15856         var align = Roo.bootstrap.Popover.alignment[placement];
15857         this.el.alignTo(on_el, align[0],align[1]);
15858         //var arrow = this.el.select('.arrow',true).first();
15859         //arrow.set(align[2], 
15860         
15861         this.el.addClass('in');
15862         
15863         
15864         if (this.el.hasClass('fade')) {
15865             // fade it?
15866         }
15867         
15868         this.fireEvent('show', this);
15869         
15870     },
15871     hide : function()
15872     {
15873         this.el.setXY([0,0]);
15874         this.el.removeClass('in');
15875         this.el.hide();
15876         this.hoverState = null;
15877         
15878         this.fireEvent('hide', this);
15879     }
15880     
15881 });
15882
15883 Roo.bootstrap.Popover.alignment = {
15884     'left' : ['r-l', [-10,0], 'right'],
15885     'right' : ['l-r', [10,0], 'left'],
15886     'bottom' : ['t-b', [0,10], 'top'],
15887     'top' : [ 'b-t', [0,-10], 'bottom']
15888 };
15889
15890  /*
15891  * - LGPL
15892  *
15893  * Progress
15894  * 
15895  */
15896
15897 /**
15898  * @class Roo.bootstrap.Progress
15899  * @extends Roo.bootstrap.Component
15900  * Bootstrap Progress class
15901  * @cfg {Boolean} striped striped of the progress bar
15902  * @cfg {Boolean} active animated of the progress bar
15903  * 
15904  * 
15905  * @constructor
15906  * Create a new Progress
15907  * @param {Object} config The config object
15908  */
15909
15910 Roo.bootstrap.Progress = function(config){
15911     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15912 };
15913
15914 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15915     
15916     striped : false,
15917     active: false,
15918     
15919     getAutoCreate : function(){
15920         var cfg = {
15921             tag: 'div',
15922             cls: 'progress'
15923         };
15924         
15925         
15926         if(this.striped){
15927             cfg.cls += ' progress-striped';
15928         }
15929       
15930         if(this.active){
15931             cfg.cls += ' active';
15932         }
15933         
15934         
15935         return cfg;
15936     }
15937    
15938 });
15939
15940  
15941
15942  /*
15943  * - LGPL
15944  *
15945  * ProgressBar
15946  * 
15947  */
15948
15949 /**
15950  * @class Roo.bootstrap.ProgressBar
15951  * @extends Roo.bootstrap.Component
15952  * Bootstrap ProgressBar class
15953  * @cfg {Number} aria_valuenow aria-value now
15954  * @cfg {Number} aria_valuemin aria-value min
15955  * @cfg {Number} aria_valuemax aria-value max
15956  * @cfg {String} label label for the progress bar
15957  * @cfg {String} panel (success | info | warning | danger )
15958  * @cfg {String} role role of the progress bar
15959  * @cfg {String} sr_only text
15960  * 
15961  * 
15962  * @constructor
15963  * Create a new ProgressBar
15964  * @param {Object} config The config object
15965  */
15966
15967 Roo.bootstrap.ProgressBar = function(config){
15968     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
15969 };
15970
15971 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
15972     
15973     aria_valuenow : 0,
15974     aria_valuemin : 0,
15975     aria_valuemax : 100,
15976     label : false,
15977     panel : false,
15978     role : false,
15979     sr_only: false,
15980     
15981     getAutoCreate : function()
15982     {
15983         
15984         var cfg = {
15985             tag: 'div',
15986             cls: 'progress-bar',
15987             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
15988         };
15989         
15990         if(this.sr_only){
15991             cfg.cn = {
15992                 tag: 'span',
15993                 cls: 'sr-only',
15994                 html: this.sr_only
15995             }
15996         }
15997         
15998         if(this.role){
15999             cfg.role = this.role;
16000         }
16001         
16002         if(this.aria_valuenow){
16003             cfg['aria-valuenow'] = this.aria_valuenow;
16004         }
16005         
16006         if(this.aria_valuemin){
16007             cfg['aria-valuemin'] = this.aria_valuemin;
16008         }
16009         
16010         if(this.aria_valuemax){
16011             cfg['aria-valuemax'] = this.aria_valuemax;
16012         }
16013         
16014         if(this.label && !this.sr_only){
16015             cfg.html = this.label;
16016         }
16017         
16018         if(this.panel){
16019             cfg.cls += ' progress-bar-' + this.panel;
16020         }
16021         
16022         return cfg;
16023     },
16024     
16025     update : function(aria_valuenow)
16026     {
16027         this.aria_valuenow = aria_valuenow;
16028         
16029         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16030     }
16031    
16032 });
16033
16034  
16035
16036  /*
16037  * - LGPL
16038  *
16039  * column
16040  * 
16041  */
16042
16043 /**
16044  * @class Roo.bootstrap.TabGroup
16045  * @extends Roo.bootstrap.Column
16046  * Bootstrap Column class
16047  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16048  * @cfg {Boolean} carousel true to make the group behave like a carousel
16049  * @cfg {Boolean} bullets show bullets for the panels
16050  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16051  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16052  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16053  * 
16054  * @constructor
16055  * Create a new TabGroup
16056  * @param {Object} config The config object
16057  */
16058
16059 Roo.bootstrap.TabGroup = function(config){
16060     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16061     if (!this.navId) {
16062         this.navId = Roo.id();
16063     }
16064     this.tabs = [];
16065     Roo.bootstrap.TabGroup.register(this);
16066     
16067 };
16068
16069 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16070     
16071     carousel : false,
16072     transition : false,
16073     bullets : 0,
16074     timer : 0,
16075     autoslide : false,
16076     slideFn : false,
16077     slideOnTouch : false,
16078     
16079     getAutoCreate : function()
16080     {
16081         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16082         
16083         cfg.cls += ' tab-content';
16084         
16085         if (this.carousel) {
16086             cfg.cls += ' carousel slide';
16087             
16088             cfg.cn = [{
16089                cls : 'carousel-inner'
16090             }];
16091         
16092             if(this.bullets  && !Roo.isTouch){
16093                 
16094                 var bullets = {
16095                     cls : 'carousel-bullets',
16096                     cn : []
16097                 };
16098                
16099                 if(this.bullets_cls){
16100                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16101                 }
16102                  /*
16103                 for (var i = 0; i < this.bullets; i++){
16104                     bullets.cn.push({
16105                         cls : 'bullet bullet-' + i
16106                     });
16107                 }
16108                 */
16109                 bullets.cn.push({
16110                     cls : 'clear'
16111                 });
16112                 
16113                 cfg.cn[0].cn = bullets;
16114             }
16115         }
16116         
16117         return cfg;
16118     },
16119     
16120     initEvents:  function()
16121     {
16122         if(Roo.isTouch && this.slideOnTouch){
16123             this.el.on("touchstart", this.onTouchStart, this);
16124         }
16125         
16126         if(this.autoslide){
16127             var _this = this;
16128             
16129             this.slideFn = window.setInterval(function() {
16130                 _this.showPanelNext();
16131             }, this.timer);
16132         }
16133         
16134     },
16135     
16136     onTouchStart : function(e, el, o)
16137     {
16138         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16139             return;
16140         }
16141         
16142         this.showPanelNext();
16143     },
16144     
16145     getChildContainer : function()
16146     {
16147         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16148     },
16149     
16150     /**
16151     * register a Navigation item
16152     * @param {Roo.bootstrap.NavItem} the navitem to add
16153     */
16154     register : function(item)
16155     {
16156         this.tabs.push( item);
16157         item.navId = this.navId; // not really needed..
16158         this.addBullet();
16159     
16160     },
16161     
16162     getActivePanel : function()
16163     {
16164         var r = false;
16165         Roo.each(this.tabs, function(t) {
16166             if (t.active) {
16167                 r = t;
16168                 return false;
16169             }
16170             return null;
16171         });
16172         return r;
16173         
16174     },
16175     getPanelByName : function(n)
16176     {
16177         var r = false;
16178         Roo.each(this.tabs, function(t) {
16179             if (t.tabId == n) {
16180                 r = t;
16181                 return false;
16182             }
16183             return null;
16184         });
16185         return r;
16186     },
16187     indexOfPanel : function(p)
16188     {
16189         var r = false;
16190         Roo.each(this.tabs, function(t,i) {
16191             if (t.tabId == p.tabId) {
16192                 r = i;
16193                 return false;
16194             }
16195             return null;
16196         });
16197         return r;
16198     },
16199     /**
16200      * show a specific panel
16201      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16202      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16203      */
16204     showPanel : function (pan)
16205     {
16206         if(this.transition || typeof(pan) == 'undefined'){
16207             Roo.log("waiting for the transitionend");
16208             return;
16209         }
16210         
16211         if (typeof(pan) == 'number') {
16212             pan = this.tabs[pan];
16213         }
16214         
16215         if (typeof(pan) == 'string') {
16216             pan = this.getPanelByName(pan);
16217         }
16218         
16219         var cur = this.getActivePanel();
16220         
16221         if(!pan || !cur){
16222             Roo.log('pan or acitve pan is undefined');
16223             return false;
16224         }
16225         
16226         if (pan.tabId == this.getActivePanel().tabId) {
16227             return true;
16228         }
16229         
16230         if (false === cur.fireEvent('beforedeactivate')) {
16231             return false;
16232         }
16233         
16234         if(this.bullets > 0 && !Roo.isTouch){
16235             this.setActiveBullet(this.indexOfPanel(pan));
16236         }
16237         
16238         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16239             
16240             this.transition = true;
16241             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16242             var lr = dir == 'next' ? 'left' : 'right';
16243             pan.el.addClass(dir); // or prev
16244             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16245             cur.el.addClass(lr); // or right
16246             pan.el.addClass(lr);
16247             
16248             var _this = this;
16249             cur.el.on('transitionend', function() {
16250                 Roo.log("trans end?");
16251                 
16252                 pan.el.removeClass([lr,dir]);
16253                 pan.setActive(true);
16254                 
16255                 cur.el.removeClass([lr]);
16256                 cur.setActive(false);
16257                 
16258                 _this.transition = false;
16259                 
16260             }, this, { single:  true } );
16261             
16262             return true;
16263         }
16264         
16265         cur.setActive(false);
16266         pan.setActive(true);
16267         
16268         return true;
16269         
16270     },
16271     showPanelNext : function()
16272     {
16273         var i = this.indexOfPanel(this.getActivePanel());
16274         
16275         if (i >= this.tabs.length - 1 && !this.autoslide) {
16276             return;
16277         }
16278         
16279         if (i >= this.tabs.length - 1 && this.autoslide) {
16280             i = -1;
16281         }
16282         
16283         this.showPanel(this.tabs[i+1]);
16284     },
16285     
16286     showPanelPrev : function()
16287     {
16288         var i = this.indexOfPanel(this.getActivePanel());
16289         
16290         if (i  < 1 && !this.autoslide) {
16291             return;
16292         }
16293         
16294         if (i < 1 && this.autoslide) {
16295             i = this.tabs.length;
16296         }
16297         
16298         this.showPanel(this.tabs[i-1]);
16299     },
16300     
16301     
16302     addBullet: function()
16303     {
16304         if(!this.bullets || Roo.isTouch){
16305             return;
16306         }
16307         var ctr = this.el.select('.carousel-bullets',true).first();
16308         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16309         var bullet = ctr.createChild({
16310             cls : 'bullet bullet-' + i
16311         },ctr.dom.lastChild);
16312         
16313         
16314         var _this = this;
16315         
16316         bullet.on('click', (function(e, el, o, ii, t){
16317
16318             e.preventDefault();
16319
16320             this.showPanel(ii);
16321
16322             if(this.autoslide && this.slideFn){
16323                 clearInterval(this.slideFn);
16324                 this.slideFn = window.setInterval(function() {
16325                     _this.showPanelNext();
16326                 }, this.timer);
16327             }
16328
16329         }).createDelegate(this, [i, bullet], true));
16330                 
16331         
16332     },
16333      
16334     setActiveBullet : function(i)
16335     {
16336         if(Roo.isTouch){
16337             return;
16338         }
16339         
16340         Roo.each(this.el.select('.bullet', true).elements, function(el){
16341             el.removeClass('selected');
16342         });
16343
16344         var bullet = this.el.select('.bullet-' + i, true).first();
16345         
16346         if(!bullet){
16347             return;
16348         }
16349         
16350         bullet.addClass('selected');
16351     }
16352     
16353     
16354   
16355 });
16356
16357  
16358
16359  
16360  
16361 Roo.apply(Roo.bootstrap.TabGroup, {
16362     
16363     groups: {},
16364      /**
16365     * register a Navigation Group
16366     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16367     */
16368     register : function(navgrp)
16369     {
16370         this.groups[navgrp.navId] = navgrp;
16371         
16372     },
16373     /**
16374     * fetch a Navigation Group based on the navigation ID
16375     * if one does not exist , it will get created.
16376     * @param {string} the navgroup to add
16377     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16378     */
16379     get: function(navId) {
16380         if (typeof(this.groups[navId]) == 'undefined') {
16381             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16382         }
16383         return this.groups[navId] ;
16384     }
16385     
16386     
16387     
16388 });
16389
16390  /*
16391  * - LGPL
16392  *
16393  * TabPanel
16394  * 
16395  */
16396
16397 /**
16398  * @class Roo.bootstrap.TabPanel
16399  * @extends Roo.bootstrap.Component
16400  * Bootstrap TabPanel class
16401  * @cfg {Boolean} active panel active
16402  * @cfg {String} html panel content
16403  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16404  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16405  * 
16406  * 
16407  * @constructor
16408  * Create a new TabPanel
16409  * @param {Object} config The config object
16410  */
16411
16412 Roo.bootstrap.TabPanel = function(config){
16413     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16414     this.addEvents({
16415         /**
16416              * @event changed
16417              * Fires when the active status changes
16418              * @param {Roo.bootstrap.TabPanel} this
16419              * @param {Boolean} state the new state
16420             
16421          */
16422         'changed': true,
16423         /**
16424              * @event beforedeactivate
16425              * Fires before a tab is de-activated - can be used to do validation on a form.
16426              * @param {Roo.bootstrap.TabPanel} this
16427              * @return {Boolean} false if there is an error
16428             
16429          */
16430         'beforedeactivate': true
16431      });
16432     
16433     this.tabId = this.tabId || Roo.id();
16434   
16435 };
16436
16437 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16438     
16439     active: false,
16440     html: false,
16441     tabId: false,
16442     navId : false,
16443     
16444     getAutoCreate : function(){
16445         var cfg = {
16446             tag: 'div',
16447             // item is needed for carousel - not sure if it has any effect otherwise
16448             cls: 'tab-pane item',
16449             html: this.html || ''
16450         };
16451         
16452         if(this.active){
16453             cfg.cls += ' active';
16454         }
16455         
16456         if(this.tabId){
16457             cfg.tabId = this.tabId;
16458         }
16459         
16460         
16461         return cfg;
16462     },
16463     
16464     initEvents:  function()
16465     {
16466         var p = this.parent();
16467         this.navId = this.navId || p.navId;
16468         
16469         if (typeof(this.navId) != 'undefined') {
16470             // not really needed.. but just in case.. parent should be a NavGroup.
16471             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16472             
16473             tg.register(this);
16474             
16475             var i = tg.tabs.length - 1;
16476             
16477             if(this.active && tg.bullets > 0 && i < tg.bullets){
16478                 tg.setActiveBullet(i);
16479             }
16480         }
16481         
16482     },
16483     
16484     
16485     onRender : function(ct, position)
16486     {
16487        // Roo.log("Call onRender: " + this.xtype);
16488         
16489         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16490         
16491         
16492         
16493         
16494         
16495     },
16496     
16497     setActive: function(state)
16498     {
16499         Roo.log("panel - set active " + this.tabId + "=" + state);
16500         
16501         this.active = state;
16502         if (!state) {
16503             this.el.removeClass('active');
16504             
16505         } else  if (!this.el.hasClass('active')) {
16506             this.el.addClass('active');
16507         }
16508         
16509         this.fireEvent('changed', this, state);
16510     }
16511     
16512     
16513 });
16514  
16515
16516  
16517
16518  /*
16519  * - LGPL
16520  *
16521  * DateField
16522  * 
16523  */
16524
16525 /**
16526  * @class Roo.bootstrap.DateField
16527  * @extends Roo.bootstrap.Input
16528  * Bootstrap DateField class
16529  * @cfg {Number} weekStart default 0
16530  * @cfg {String} viewMode default empty, (months|years)
16531  * @cfg {String} minViewMode default empty, (months|years)
16532  * @cfg {Number} startDate default -Infinity
16533  * @cfg {Number} endDate default Infinity
16534  * @cfg {Boolean} todayHighlight default false
16535  * @cfg {Boolean} todayBtn default false
16536  * @cfg {Boolean} calendarWeeks default false
16537  * @cfg {Object} daysOfWeekDisabled default empty
16538  * @cfg {Boolean} singleMode default false (true | false)
16539  * 
16540  * @cfg {Boolean} keyboardNavigation default true
16541  * @cfg {String} language default en
16542  * 
16543  * @constructor
16544  * Create a new DateField
16545  * @param {Object} config The config object
16546  */
16547
16548 Roo.bootstrap.DateField = function(config){
16549     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16550      this.addEvents({
16551             /**
16552              * @event show
16553              * Fires when this field show.
16554              * @param {Roo.bootstrap.DateField} this
16555              * @param {Mixed} date The date value
16556              */
16557             show : true,
16558             /**
16559              * @event show
16560              * Fires when this field hide.
16561              * @param {Roo.bootstrap.DateField} this
16562              * @param {Mixed} date The date value
16563              */
16564             hide : true,
16565             /**
16566              * @event select
16567              * Fires when select a date.
16568              * @param {Roo.bootstrap.DateField} this
16569              * @param {Mixed} date The date value
16570              */
16571             select : true,
16572             /**
16573              * @event beforeselect
16574              * Fires when before select a date.
16575              * @param {Roo.bootstrap.DateField} this
16576              * @param {Mixed} date The date value
16577              */
16578             beforeselect : true
16579         });
16580 };
16581
16582 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16583     
16584     /**
16585      * @cfg {String} format
16586      * The default date format string which can be overriden for localization support.  The format must be
16587      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16588      */
16589     format : "m/d/y",
16590     /**
16591      * @cfg {String} altFormats
16592      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16593      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16594      */
16595     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16596     
16597     weekStart : 0,
16598     
16599     viewMode : '',
16600     
16601     minViewMode : '',
16602     
16603     todayHighlight : false,
16604     
16605     todayBtn: false,
16606     
16607     language: 'en',
16608     
16609     keyboardNavigation: true,
16610     
16611     calendarWeeks: false,
16612     
16613     startDate: -Infinity,
16614     
16615     endDate: Infinity,
16616     
16617     daysOfWeekDisabled: [],
16618     
16619     _events: [],
16620     
16621     singleMode : false,
16622     
16623     UTCDate: function()
16624     {
16625         return new Date(Date.UTC.apply(Date, arguments));
16626     },
16627     
16628     UTCToday: function()
16629     {
16630         var today = new Date();
16631         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16632     },
16633     
16634     getDate: function() {
16635             var d = this.getUTCDate();
16636             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16637     },
16638     
16639     getUTCDate: function() {
16640             return this.date;
16641     },
16642     
16643     setDate: function(d) {
16644             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16645     },
16646     
16647     setUTCDate: function(d) {
16648             this.date = d;
16649             this.setValue(this.formatDate(this.date));
16650     },
16651         
16652     onRender: function(ct, position)
16653     {
16654         
16655         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16656         
16657         this.language = this.language || 'en';
16658         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16659         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16660         
16661         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16662         this.format = this.format || 'm/d/y';
16663         this.isInline = false;
16664         this.isInput = true;
16665         this.component = this.el.select('.add-on', true).first() || false;
16666         this.component = (this.component && this.component.length === 0) ? false : this.component;
16667         this.hasInput = this.component && this.inputEL().length;
16668         
16669         if (typeof(this.minViewMode === 'string')) {
16670             switch (this.minViewMode) {
16671                 case 'months':
16672                     this.minViewMode = 1;
16673                     break;
16674                 case 'years':
16675                     this.minViewMode = 2;
16676                     break;
16677                 default:
16678                     this.minViewMode = 0;
16679                     break;
16680             }
16681         }
16682         
16683         if (typeof(this.viewMode === 'string')) {
16684             switch (this.viewMode) {
16685                 case 'months':
16686                     this.viewMode = 1;
16687                     break;
16688                 case 'years':
16689                     this.viewMode = 2;
16690                     break;
16691                 default:
16692                     this.viewMode = 0;
16693                     break;
16694             }
16695         }
16696                 
16697         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16698         
16699 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16700         
16701         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16702         
16703         this.picker().on('mousedown', this.onMousedown, this);
16704         this.picker().on('click', this.onClick, this);
16705         
16706         this.picker().addClass('datepicker-dropdown');
16707         
16708         this.startViewMode = this.viewMode;
16709         
16710         if(this.singleMode){
16711             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16712                 v.setVisibilityMode(Roo.Element.DISPLAY);
16713                 v.hide();
16714             });
16715             
16716             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16717                 v.setStyle('width', '189px');
16718             });
16719         }
16720         
16721         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16722             if(!this.calendarWeeks){
16723                 v.remove();
16724                 return;
16725             }
16726             
16727             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16728             v.attr('colspan', function(i, val){
16729                 return parseInt(val) + 1;
16730             });
16731         });
16732                         
16733         
16734         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16735         
16736         this.setStartDate(this.startDate);
16737         this.setEndDate(this.endDate);
16738         
16739         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16740         
16741         this.fillDow();
16742         this.fillMonths();
16743         this.update();
16744         this.showMode();
16745         
16746         if(this.isInline) {
16747             this.show();
16748         }
16749     },
16750     
16751     picker : function()
16752     {
16753         return this.pickerEl;
16754 //        return this.el.select('.datepicker', true).first();
16755     },
16756     
16757     fillDow: function()
16758     {
16759         var dowCnt = this.weekStart;
16760         
16761         var dow = {
16762             tag: 'tr',
16763             cn: [
16764                 
16765             ]
16766         };
16767         
16768         if(this.calendarWeeks){
16769             dow.cn.push({
16770                 tag: 'th',
16771                 cls: 'cw',
16772                 html: '&nbsp;'
16773             })
16774         }
16775         
16776         while (dowCnt < this.weekStart + 7) {
16777             dow.cn.push({
16778                 tag: 'th',
16779                 cls: 'dow',
16780                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16781             });
16782         }
16783         
16784         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16785     },
16786     
16787     fillMonths: function()
16788     {    
16789         var i = 0;
16790         var months = this.picker().select('>.datepicker-months td', true).first();
16791         
16792         months.dom.innerHTML = '';
16793         
16794         while (i < 12) {
16795             var month = {
16796                 tag: 'span',
16797                 cls: 'month',
16798                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16799             };
16800             
16801             months.createChild(month);
16802         }
16803         
16804     },
16805     
16806     update: function()
16807     {
16808         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;
16809         
16810         if (this.date < this.startDate) {
16811             this.viewDate = new Date(this.startDate);
16812         } else if (this.date > this.endDate) {
16813             this.viewDate = new Date(this.endDate);
16814         } else {
16815             this.viewDate = new Date(this.date);
16816         }
16817         
16818         this.fill();
16819     },
16820     
16821     fill: function() 
16822     {
16823         var d = new Date(this.viewDate),
16824                 year = d.getUTCFullYear(),
16825                 month = d.getUTCMonth(),
16826                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16827                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16828                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16829                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16830                 currentDate = this.date && this.date.valueOf(),
16831                 today = this.UTCToday();
16832         
16833         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16834         
16835 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16836         
16837 //        this.picker.select('>tfoot th.today').
16838 //                                              .text(dates[this.language].today)
16839 //                                              .toggle(this.todayBtn !== false);
16840     
16841         this.updateNavArrows();
16842         this.fillMonths();
16843                                                 
16844         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16845         
16846         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16847          
16848         prevMonth.setUTCDate(day);
16849         
16850         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16851         
16852         var nextMonth = new Date(prevMonth);
16853         
16854         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16855         
16856         nextMonth = nextMonth.valueOf();
16857         
16858         var fillMonths = false;
16859         
16860         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16861         
16862         while(prevMonth.valueOf() < nextMonth) {
16863             var clsName = '';
16864             
16865             if (prevMonth.getUTCDay() === this.weekStart) {
16866                 if(fillMonths){
16867                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16868                 }
16869                     
16870                 fillMonths = {
16871                     tag: 'tr',
16872                     cn: []
16873                 };
16874                 
16875                 if(this.calendarWeeks){
16876                     // ISO 8601: First week contains first thursday.
16877                     // ISO also states week starts on Monday, but we can be more abstract here.
16878                     var
16879                     // Start of current week: based on weekstart/current date
16880                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16881                     // Thursday of this week
16882                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16883                     // First Thursday of year, year from thursday
16884                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16885                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16886                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16887                     
16888                     fillMonths.cn.push({
16889                         tag: 'td',
16890                         cls: 'cw',
16891                         html: calWeek
16892                     });
16893                 }
16894             }
16895             
16896             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16897                 clsName += ' old';
16898             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16899                 clsName += ' new';
16900             }
16901             if (this.todayHighlight &&
16902                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16903                 prevMonth.getUTCMonth() == today.getMonth() &&
16904                 prevMonth.getUTCDate() == today.getDate()) {
16905                 clsName += ' today';
16906             }
16907             
16908             if (currentDate && prevMonth.valueOf() === currentDate) {
16909                 clsName += ' active';
16910             }
16911             
16912             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16913                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16914                     clsName += ' disabled';
16915             }
16916             
16917             fillMonths.cn.push({
16918                 tag: 'td',
16919                 cls: 'day ' + clsName,
16920                 html: prevMonth.getDate()
16921             });
16922             
16923             prevMonth.setDate(prevMonth.getDate()+1);
16924         }
16925           
16926         var currentYear = this.date && this.date.getUTCFullYear();
16927         var currentMonth = this.date && this.date.getUTCMonth();
16928         
16929         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
16930         
16931         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
16932             v.removeClass('active');
16933             
16934             if(currentYear === year && k === currentMonth){
16935                 v.addClass('active');
16936             }
16937             
16938             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
16939                 v.addClass('disabled');
16940             }
16941             
16942         });
16943         
16944         
16945         year = parseInt(year/10, 10) * 10;
16946         
16947         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
16948         
16949         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
16950         
16951         year -= 1;
16952         for (var i = -1; i < 11; i++) {
16953             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
16954                 tag: 'span',
16955                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
16956                 html: year
16957             });
16958             
16959             year += 1;
16960         }
16961     },
16962     
16963     showMode: function(dir) 
16964     {
16965         if (dir) {
16966             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
16967         }
16968         
16969         Roo.each(this.picker().select('>div',true).elements, function(v){
16970             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16971             v.hide();
16972         });
16973         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
16974     },
16975     
16976     place: function()
16977     {
16978         if(this.isInline) {
16979             return;
16980         }
16981         
16982         this.picker().removeClass(['bottom', 'top']);
16983         
16984         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
16985             /*
16986              * place to the top of element!
16987              *
16988              */
16989             
16990             this.picker().addClass('top');
16991             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
16992             
16993             return;
16994         }
16995         
16996         this.picker().addClass('bottom');
16997         
16998         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
16999     },
17000     
17001     parseDate : function(value)
17002     {
17003         if(!value || value instanceof Date){
17004             return value;
17005         }
17006         var v = Date.parseDate(value, this.format);
17007         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17008             v = Date.parseDate(value, 'Y-m-d');
17009         }
17010         if(!v && this.altFormats){
17011             if(!this.altFormatsArray){
17012                 this.altFormatsArray = this.altFormats.split("|");
17013             }
17014             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17015                 v = Date.parseDate(value, this.altFormatsArray[i]);
17016             }
17017         }
17018         return v;
17019     },
17020     
17021     formatDate : function(date, fmt)
17022     {   
17023         return (!date || !(date instanceof Date)) ?
17024         date : date.dateFormat(fmt || this.format);
17025     },
17026     
17027     onFocus : function()
17028     {
17029         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17030         this.show();
17031     },
17032     
17033     onBlur : function()
17034     {
17035         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17036         
17037         var d = this.inputEl().getValue();
17038         
17039         this.setValue(d);
17040                 
17041         this.hide();
17042     },
17043     
17044     show : function()
17045     {
17046         this.picker().show();
17047         this.update();
17048         this.place();
17049         
17050         this.fireEvent('show', this, this.date);
17051     },
17052     
17053     hide : function()
17054     {
17055         if(this.isInline) {
17056             return;
17057         }
17058         this.picker().hide();
17059         this.viewMode = this.startViewMode;
17060         this.showMode();
17061         
17062         this.fireEvent('hide', this, this.date);
17063         
17064     },
17065     
17066     onMousedown: function(e)
17067     {
17068         e.stopPropagation();
17069         e.preventDefault();
17070     },
17071     
17072     keyup: function(e)
17073     {
17074         Roo.bootstrap.DateField.superclass.keyup.call(this);
17075         this.update();
17076     },
17077
17078     setValue: function(v)
17079     {
17080         if(this.fireEvent('beforeselect', this, v) !== false){
17081             var d = new Date(this.parseDate(v) ).clearTime();
17082         
17083             if(isNaN(d.getTime())){
17084                 this.date = this.viewDate = '';
17085                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17086                 return;
17087             }
17088
17089             v = this.formatDate(d);
17090
17091             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17092
17093             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17094
17095             this.update();
17096
17097             this.fireEvent('select', this, this.date);
17098         }
17099     },
17100     
17101     getValue: function()
17102     {
17103         return this.formatDate(this.date);
17104     },
17105     
17106     fireKey: function(e)
17107     {
17108         if (!this.picker().isVisible()){
17109             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17110                 this.show();
17111             }
17112             return;
17113         }
17114         
17115         var dateChanged = false,
17116         dir, day, month,
17117         newDate, newViewDate;
17118         
17119         switch(e.keyCode){
17120             case 27: // escape
17121                 this.hide();
17122                 e.preventDefault();
17123                 break;
17124             case 37: // left
17125             case 39: // right
17126                 if (!this.keyboardNavigation) {
17127                     break;
17128                 }
17129                 dir = e.keyCode == 37 ? -1 : 1;
17130                 
17131                 if (e.ctrlKey){
17132                     newDate = this.moveYear(this.date, dir);
17133                     newViewDate = this.moveYear(this.viewDate, dir);
17134                 } else if (e.shiftKey){
17135                     newDate = this.moveMonth(this.date, dir);
17136                     newViewDate = this.moveMonth(this.viewDate, dir);
17137                 } else {
17138                     newDate = new Date(this.date);
17139                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17140                     newViewDate = new Date(this.viewDate);
17141                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17142                 }
17143                 if (this.dateWithinRange(newDate)){
17144                     this.date = newDate;
17145                     this.viewDate = newViewDate;
17146                     this.setValue(this.formatDate(this.date));
17147 //                    this.update();
17148                     e.preventDefault();
17149                     dateChanged = true;
17150                 }
17151                 break;
17152             case 38: // up
17153             case 40: // down
17154                 if (!this.keyboardNavigation) {
17155                     break;
17156                 }
17157                 dir = e.keyCode == 38 ? -1 : 1;
17158                 if (e.ctrlKey){
17159                     newDate = this.moveYear(this.date, dir);
17160                     newViewDate = this.moveYear(this.viewDate, dir);
17161                 } else if (e.shiftKey){
17162                     newDate = this.moveMonth(this.date, dir);
17163                     newViewDate = this.moveMonth(this.viewDate, dir);
17164                 } else {
17165                     newDate = new Date(this.date);
17166                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17167                     newViewDate = new Date(this.viewDate);
17168                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17169                 }
17170                 if (this.dateWithinRange(newDate)){
17171                     this.date = newDate;
17172                     this.viewDate = newViewDate;
17173                     this.setValue(this.formatDate(this.date));
17174 //                    this.update();
17175                     e.preventDefault();
17176                     dateChanged = true;
17177                 }
17178                 break;
17179             case 13: // enter
17180                 this.setValue(this.formatDate(this.date));
17181                 this.hide();
17182                 e.preventDefault();
17183                 break;
17184             case 9: // tab
17185                 this.setValue(this.formatDate(this.date));
17186                 this.hide();
17187                 break;
17188             case 16: // shift
17189             case 17: // ctrl
17190             case 18: // alt
17191                 break;
17192             default :
17193                 this.hide();
17194                 
17195         }
17196     },
17197     
17198     
17199     onClick: function(e) 
17200     {
17201         e.stopPropagation();
17202         e.preventDefault();
17203         
17204         var target = e.getTarget();
17205         
17206         if(target.nodeName.toLowerCase() === 'i'){
17207             target = Roo.get(target).dom.parentNode;
17208         }
17209         
17210         var nodeName = target.nodeName;
17211         var className = target.className;
17212         var html = target.innerHTML;
17213         //Roo.log(nodeName);
17214         
17215         switch(nodeName.toLowerCase()) {
17216             case 'th':
17217                 switch(className) {
17218                     case 'switch':
17219                         this.showMode(1);
17220                         break;
17221                     case 'prev':
17222                     case 'next':
17223                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17224                         switch(this.viewMode){
17225                                 case 0:
17226                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17227                                         break;
17228                                 case 1:
17229                                 case 2:
17230                                         this.viewDate = this.moveYear(this.viewDate, dir);
17231                                         break;
17232                         }
17233                         this.fill();
17234                         break;
17235                     case 'today':
17236                         var date = new Date();
17237                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17238 //                        this.fill()
17239                         this.setValue(this.formatDate(this.date));
17240                         
17241                         this.hide();
17242                         break;
17243                 }
17244                 break;
17245             case 'span':
17246                 if (className.indexOf('disabled') < 0) {
17247                     this.viewDate.setUTCDate(1);
17248                     if (className.indexOf('month') > -1) {
17249                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17250                     } else {
17251                         var year = parseInt(html, 10) || 0;
17252                         this.viewDate.setUTCFullYear(year);
17253                         
17254                     }
17255                     
17256                     if(this.singleMode){
17257                         this.setValue(this.formatDate(this.viewDate));
17258                         this.hide();
17259                         return;
17260                     }
17261                     
17262                     this.showMode(-1);
17263                     this.fill();
17264                 }
17265                 break;
17266                 
17267             case 'td':
17268                 //Roo.log(className);
17269                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17270                     var day = parseInt(html, 10) || 1;
17271                     var year = this.viewDate.getUTCFullYear(),
17272                         month = this.viewDate.getUTCMonth();
17273
17274                     if (className.indexOf('old') > -1) {
17275                         if(month === 0 ){
17276                             month = 11;
17277                             year -= 1;
17278                         }else{
17279                             month -= 1;
17280                         }
17281                     } else if (className.indexOf('new') > -1) {
17282                         if (month == 11) {
17283                             month = 0;
17284                             year += 1;
17285                         } else {
17286                             month += 1;
17287                         }
17288                     }
17289                     //Roo.log([year,month,day]);
17290                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17291                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17292 //                    this.fill();
17293                     //Roo.log(this.formatDate(this.date));
17294                     this.setValue(this.formatDate(this.date));
17295                     this.hide();
17296                 }
17297                 break;
17298         }
17299     },
17300     
17301     setStartDate: function(startDate)
17302     {
17303         this.startDate = startDate || -Infinity;
17304         if (this.startDate !== -Infinity) {
17305             this.startDate = this.parseDate(this.startDate);
17306         }
17307         this.update();
17308         this.updateNavArrows();
17309     },
17310
17311     setEndDate: function(endDate)
17312     {
17313         this.endDate = endDate || Infinity;
17314         if (this.endDate !== Infinity) {
17315             this.endDate = this.parseDate(this.endDate);
17316         }
17317         this.update();
17318         this.updateNavArrows();
17319     },
17320     
17321     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17322     {
17323         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17324         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17325             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17326         }
17327         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17328             return parseInt(d, 10);
17329         });
17330         this.update();
17331         this.updateNavArrows();
17332     },
17333     
17334     updateNavArrows: function() 
17335     {
17336         if(this.singleMode){
17337             return;
17338         }
17339         
17340         var d = new Date(this.viewDate),
17341         year = d.getUTCFullYear(),
17342         month = d.getUTCMonth();
17343         
17344         Roo.each(this.picker().select('.prev', true).elements, function(v){
17345             v.show();
17346             switch (this.viewMode) {
17347                 case 0:
17348
17349                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17350                         v.hide();
17351                     }
17352                     break;
17353                 case 1:
17354                 case 2:
17355                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17356                         v.hide();
17357                     }
17358                     break;
17359             }
17360         });
17361         
17362         Roo.each(this.picker().select('.next', true).elements, function(v){
17363             v.show();
17364             switch (this.viewMode) {
17365                 case 0:
17366
17367                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17368                         v.hide();
17369                     }
17370                     break;
17371                 case 1:
17372                 case 2:
17373                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17374                         v.hide();
17375                     }
17376                     break;
17377             }
17378         })
17379     },
17380     
17381     moveMonth: function(date, dir)
17382     {
17383         if (!dir) {
17384             return date;
17385         }
17386         var new_date = new Date(date.valueOf()),
17387         day = new_date.getUTCDate(),
17388         month = new_date.getUTCMonth(),
17389         mag = Math.abs(dir),
17390         new_month, test;
17391         dir = dir > 0 ? 1 : -1;
17392         if (mag == 1){
17393             test = dir == -1
17394             // If going back one month, make sure month is not current month
17395             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17396             ? function(){
17397                 return new_date.getUTCMonth() == month;
17398             }
17399             // If going forward one month, make sure month is as expected
17400             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17401             : function(){
17402                 return new_date.getUTCMonth() != new_month;
17403             };
17404             new_month = month + dir;
17405             new_date.setUTCMonth(new_month);
17406             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17407             if (new_month < 0 || new_month > 11) {
17408                 new_month = (new_month + 12) % 12;
17409             }
17410         } else {
17411             // For magnitudes >1, move one month at a time...
17412             for (var i=0; i<mag; i++) {
17413                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17414                 new_date = this.moveMonth(new_date, dir);
17415             }
17416             // ...then reset the day, keeping it in the new month
17417             new_month = new_date.getUTCMonth();
17418             new_date.setUTCDate(day);
17419             test = function(){
17420                 return new_month != new_date.getUTCMonth();
17421             };
17422         }
17423         // Common date-resetting loop -- if date is beyond end of month, make it
17424         // end of month
17425         while (test()){
17426             new_date.setUTCDate(--day);
17427             new_date.setUTCMonth(new_month);
17428         }
17429         return new_date;
17430     },
17431
17432     moveYear: function(date, dir)
17433     {
17434         return this.moveMonth(date, dir*12);
17435     },
17436
17437     dateWithinRange: function(date)
17438     {
17439         return date >= this.startDate && date <= this.endDate;
17440     },
17441
17442     
17443     remove: function() 
17444     {
17445         this.picker().remove();
17446     }
17447    
17448 });
17449
17450 Roo.apply(Roo.bootstrap.DateField,  {
17451     
17452     head : {
17453         tag: 'thead',
17454         cn: [
17455         {
17456             tag: 'tr',
17457             cn: [
17458             {
17459                 tag: 'th',
17460                 cls: 'prev',
17461                 html: '<i class="fa fa-arrow-left"/>'
17462             },
17463             {
17464                 tag: 'th',
17465                 cls: 'switch',
17466                 colspan: '5'
17467             },
17468             {
17469                 tag: 'th',
17470                 cls: 'next',
17471                 html: '<i class="fa fa-arrow-right"/>'
17472             }
17473
17474             ]
17475         }
17476         ]
17477     },
17478     
17479     content : {
17480         tag: 'tbody',
17481         cn: [
17482         {
17483             tag: 'tr',
17484             cn: [
17485             {
17486                 tag: 'td',
17487                 colspan: '7'
17488             }
17489             ]
17490         }
17491         ]
17492     },
17493     
17494     footer : {
17495         tag: 'tfoot',
17496         cn: [
17497         {
17498             tag: 'tr',
17499             cn: [
17500             {
17501                 tag: 'th',
17502                 colspan: '7',
17503                 cls: 'today'
17504             }
17505                     
17506             ]
17507         }
17508         ]
17509     },
17510     
17511     dates:{
17512         en: {
17513             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17514             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17515             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17516             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17517             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17518             today: "Today"
17519         }
17520     },
17521     
17522     modes: [
17523     {
17524         clsName: 'days',
17525         navFnc: 'Month',
17526         navStep: 1
17527     },
17528     {
17529         clsName: 'months',
17530         navFnc: 'FullYear',
17531         navStep: 1
17532     },
17533     {
17534         clsName: 'years',
17535         navFnc: 'FullYear',
17536         navStep: 10
17537     }]
17538 });
17539
17540 Roo.apply(Roo.bootstrap.DateField,  {
17541   
17542     template : {
17543         tag: 'div',
17544         cls: 'datepicker dropdown-menu roo-dynamic',
17545         cn: [
17546         {
17547             tag: 'div',
17548             cls: 'datepicker-days',
17549             cn: [
17550             {
17551                 tag: 'table',
17552                 cls: 'table-condensed',
17553                 cn:[
17554                 Roo.bootstrap.DateField.head,
17555                 {
17556                     tag: 'tbody'
17557                 },
17558                 Roo.bootstrap.DateField.footer
17559                 ]
17560             }
17561             ]
17562         },
17563         {
17564             tag: 'div',
17565             cls: 'datepicker-months',
17566             cn: [
17567             {
17568                 tag: 'table',
17569                 cls: 'table-condensed',
17570                 cn:[
17571                 Roo.bootstrap.DateField.head,
17572                 Roo.bootstrap.DateField.content,
17573                 Roo.bootstrap.DateField.footer
17574                 ]
17575             }
17576             ]
17577         },
17578         {
17579             tag: 'div',
17580             cls: 'datepicker-years',
17581             cn: [
17582             {
17583                 tag: 'table',
17584                 cls: 'table-condensed',
17585                 cn:[
17586                 Roo.bootstrap.DateField.head,
17587                 Roo.bootstrap.DateField.content,
17588                 Roo.bootstrap.DateField.footer
17589                 ]
17590             }
17591             ]
17592         }
17593         ]
17594     }
17595 });
17596
17597  
17598
17599  /*
17600  * - LGPL
17601  *
17602  * TimeField
17603  * 
17604  */
17605
17606 /**
17607  * @class Roo.bootstrap.TimeField
17608  * @extends Roo.bootstrap.Input
17609  * Bootstrap DateField class
17610  * 
17611  * 
17612  * @constructor
17613  * Create a new TimeField
17614  * @param {Object} config The config object
17615  */
17616
17617 Roo.bootstrap.TimeField = function(config){
17618     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17619     this.addEvents({
17620             /**
17621              * @event show
17622              * Fires when this field show.
17623              * @param {Roo.bootstrap.DateField} thisthis
17624              * @param {Mixed} date The date value
17625              */
17626             show : true,
17627             /**
17628              * @event show
17629              * Fires when this field hide.
17630              * @param {Roo.bootstrap.DateField} this
17631              * @param {Mixed} date The date value
17632              */
17633             hide : true,
17634             /**
17635              * @event select
17636              * Fires when select a date.
17637              * @param {Roo.bootstrap.DateField} this
17638              * @param {Mixed} date The date value
17639              */
17640             select : true
17641         });
17642 };
17643
17644 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17645     
17646     /**
17647      * @cfg {String} format
17648      * The default time format string which can be overriden for localization support.  The format must be
17649      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17650      */
17651     format : "H:i",
17652        
17653     onRender: function(ct, position)
17654     {
17655         
17656         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17657                 
17658         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17659         
17660         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17661         
17662         this.pop = this.picker().select('>.datepicker-time',true).first();
17663         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17664         
17665         this.picker().on('mousedown', this.onMousedown, this);
17666         this.picker().on('click', this.onClick, this);
17667         
17668         this.picker().addClass('datepicker-dropdown');
17669     
17670         this.fillTime();
17671         this.update();
17672             
17673         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17674         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17675         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17676         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17677         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17678         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17679
17680     },
17681     
17682     fireKey: function(e){
17683         if (!this.picker().isVisible()){
17684             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17685                 this.show();
17686             }
17687             return;
17688         }
17689
17690         e.preventDefault();
17691         
17692         switch(e.keyCode){
17693             case 27: // escape
17694                 this.hide();
17695                 break;
17696             case 37: // left
17697             case 39: // right
17698                 this.onTogglePeriod();
17699                 break;
17700             case 38: // up
17701                 this.onIncrementMinutes();
17702                 break;
17703             case 40: // down
17704                 this.onDecrementMinutes();
17705                 break;
17706             case 13: // enter
17707             case 9: // tab
17708                 this.setTime();
17709                 break;
17710         }
17711     },
17712     
17713     onClick: function(e) {
17714         e.stopPropagation();
17715         e.preventDefault();
17716     },
17717     
17718     picker : function()
17719     {
17720         return this.el.select('.datepicker', true).first();
17721     },
17722     
17723     fillTime: function()
17724     {    
17725         var time = this.pop.select('tbody', true).first();
17726         
17727         time.dom.innerHTML = '';
17728         
17729         time.createChild({
17730             tag: 'tr',
17731             cn: [
17732                 {
17733                     tag: 'td',
17734                     cn: [
17735                         {
17736                             tag: 'a',
17737                             href: '#',
17738                             cls: 'btn',
17739                             cn: [
17740                                 {
17741                                     tag: 'span',
17742                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17743                                 }
17744                             ]
17745                         } 
17746                     ]
17747                 },
17748                 {
17749                     tag: 'td',
17750                     cls: 'separator'
17751                 },
17752                 {
17753                     tag: 'td',
17754                     cn: [
17755                         {
17756                             tag: 'a',
17757                             href: '#',
17758                             cls: 'btn',
17759                             cn: [
17760                                 {
17761                                     tag: 'span',
17762                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17763                                 }
17764                             ]
17765                         }
17766                     ]
17767                 },
17768                 {
17769                     tag: 'td',
17770                     cls: 'separator'
17771                 }
17772             ]
17773         });
17774         
17775         time.createChild({
17776             tag: 'tr',
17777             cn: [
17778                 {
17779                     tag: 'td',
17780                     cn: [
17781                         {
17782                             tag: 'span',
17783                             cls: 'timepicker-hour',
17784                             html: '00'
17785                         }  
17786                     ]
17787                 },
17788                 {
17789                     tag: 'td',
17790                     cls: 'separator',
17791                     html: ':'
17792                 },
17793                 {
17794                     tag: 'td',
17795                     cn: [
17796                         {
17797                             tag: 'span',
17798                             cls: 'timepicker-minute',
17799                             html: '00'
17800                         }  
17801                     ]
17802                 },
17803                 {
17804                     tag: 'td',
17805                     cls: 'separator'
17806                 },
17807                 {
17808                     tag: 'td',
17809                     cn: [
17810                         {
17811                             tag: 'button',
17812                             type: 'button',
17813                             cls: 'btn btn-primary period',
17814                             html: 'AM'
17815                             
17816                         }
17817                     ]
17818                 }
17819             ]
17820         });
17821         
17822         time.createChild({
17823             tag: 'tr',
17824             cn: [
17825                 {
17826                     tag: 'td',
17827                     cn: [
17828                         {
17829                             tag: 'a',
17830                             href: '#',
17831                             cls: 'btn',
17832                             cn: [
17833                                 {
17834                                     tag: 'span',
17835                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17836                                 }
17837                             ]
17838                         }
17839                     ]
17840                 },
17841                 {
17842                     tag: 'td',
17843                     cls: 'separator'
17844                 },
17845                 {
17846                     tag: 'td',
17847                     cn: [
17848                         {
17849                             tag: 'a',
17850                             href: '#',
17851                             cls: 'btn',
17852                             cn: [
17853                                 {
17854                                     tag: 'span',
17855                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17856                                 }
17857                             ]
17858                         }
17859                     ]
17860                 },
17861                 {
17862                     tag: 'td',
17863                     cls: 'separator'
17864                 }
17865             ]
17866         });
17867         
17868     },
17869     
17870     update: function()
17871     {
17872         
17873         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17874         
17875         this.fill();
17876     },
17877     
17878     fill: function() 
17879     {
17880         var hours = this.time.getHours();
17881         var minutes = this.time.getMinutes();
17882         var period = 'AM';
17883         
17884         if(hours > 11){
17885             period = 'PM';
17886         }
17887         
17888         if(hours == 0){
17889             hours = 12;
17890         }
17891         
17892         
17893         if(hours > 12){
17894             hours = hours - 12;
17895         }
17896         
17897         if(hours < 10){
17898             hours = '0' + hours;
17899         }
17900         
17901         if(minutes < 10){
17902             minutes = '0' + minutes;
17903         }
17904         
17905         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17906         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17907         this.pop.select('button', true).first().dom.innerHTML = period;
17908         
17909     },
17910     
17911     place: function()
17912     {   
17913         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17914         
17915         var cls = ['bottom'];
17916         
17917         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17918             cls.pop();
17919             cls.push('top');
17920         }
17921         
17922         cls.push('right');
17923         
17924         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
17925             cls.pop();
17926             cls.push('left');
17927         }
17928         
17929         this.picker().addClass(cls.join('-'));
17930         
17931         var _this = this;
17932         
17933         Roo.each(cls, function(c){
17934             if(c == 'bottom'){
17935                 _this.picker().setTop(_this.inputEl().getHeight());
17936                 return;
17937             }
17938             if(c == 'top'){
17939                 _this.picker().setTop(0 - _this.picker().getHeight());
17940                 return;
17941             }
17942             
17943             if(c == 'left'){
17944                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
17945                 return;
17946             }
17947             if(c == 'right'){
17948                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
17949                 return;
17950             }
17951         });
17952         
17953     },
17954   
17955     onFocus : function()
17956     {
17957         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
17958         this.show();
17959     },
17960     
17961     onBlur : function()
17962     {
17963         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
17964         this.hide();
17965     },
17966     
17967     show : function()
17968     {
17969         this.picker().show();
17970         this.pop.show();
17971         this.update();
17972         this.place();
17973         
17974         this.fireEvent('show', this, this.date);
17975     },
17976     
17977     hide : function()
17978     {
17979         this.picker().hide();
17980         this.pop.hide();
17981         
17982         this.fireEvent('hide', this, this.date);
17983     },
17984     
17985     setTime : function()
17986     {
17987         this.hide();
17988         this.setValue(this.time.format(this.format));
17989         
17990         this.fireEvent('select', this, this.date);
17991         
17992         
17993     },
17994     
17995     onMousedown: function(e){
17996         e.stopPropagation();
17997         e.preventDefault();
17998     },
17999     
18000     onIncrementHours: function()
18001     {
18002         Roo.log('onIncrementHours');
18003         this.time = this.time.add(Date.HOUR, 1);
18004         this.update();
18005         
18006     },
18007     
18008     onDecrementHours: function()
18009     {
18010         Roo.log('onDecrementHours');
18011         this.time = this.time.add(Date.HOUR, -1);
18012         this.update();
18013     },
18014     
18015     onIncrementMinutes: function()
18016     {
18017         Roo.log('onIncrementMinutes');
18018         this.time = this.time.add(Date.MINUTE, 1);
18019         this.update();
18020     },
18021     
18022     onDecrementMinutes: function()
18023     {
18024         Roo.log('onDecrementMinutes');
18025         this.time = this.time.add(Date.MINUTE, -1);
18026         this.update();
18027     },
18028     
18029     onTogglePeriod: function()
18030     {
18031         Roo.log('onTogglePeriod');
18032         this.time = this.time.add(Date.HOUR, 12);
18033         this.update();
18034     }
18035     
18036    
18037 });
18038
18039 Roo.apply(Roo.bootstrap.TimeField,  {
18040     
18041     content : {
18042         tag: 'tbody',
18043         cn: [
18044             {
18045                 tag: 'tr',
18046                 cn: [
18047                 {
18048                     tag: 'td',
18049                     colspan: '7'
18050                 }
18051                 ]
18052             }
18053         ]
18054     },
18055     
18056     footer : {
18057         tag: 'tfoot',
18058         cn: [
18059             {
18060                 tag: 'tr',
18061                 cn: [
18062                 {
18063                     tag: 'th',
18064                     colspan: '7',
18065                     cls: '',
18066                     cn: [
18067                         {
18068                             tag: 'button',
18069                             cls: 'btn btn-info ok',
18070                             html: 'OK'
18071                         }
18072                     ]
18073                 }
18074
18075                 ]
18076             }
18077         ]
18078     }
18079 });
18080
18081 Roo.apply(Roo.bootstrap.TimeField,  {
18082   
18083     template : {
18084         tag: 'div',
18085         cls: 'datepicker dropdown-menu',
18086         cn: [
18087             {
18088                 tag: 'div',
18089                 cls: 'datepicker-time',
18090                 cn: [
18091                 {
18092                     tag: 'table',
18093                     cls: 'table-condensed',
18094                     cn:[
18095                     Roo.bootstrap.TimeField.content,
18096                     Roo.bootstrap.TimeField.footer
18097                     ]
18098                 }
18099                 ]
18100             }
18101         ]
18102     }
18103 });
18104
18105  
18106
18107  /*
18108  * - LGPL
18109  *
18110  * MonthField
18111  * 
18112  */
18113
18114 /**
18115  * @class Roo.bootstrap.MonthField
18116  * @extends Roo.bootstrap.Input
18117  * Bootstrap MonthField class
18118  * 
18119  * @cfg {String} language default en
18120  * 
18121  * @constructor
18122  * Create a new MonthField
18123  * @param {Object} config The config object
18124  */
18125
18126 Roo.bootstrap.MonthField = function(config){
18127     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18128     
18129     this.addEvents({
18130         /**
18131          * @event show
18132          * Fires when this field show.
18133          * @param {Roo.bootstrap.MonthField} this
18134          * @param {Mixed} date The date value
18135          */
18136         show : true,
18137         /**
18138          * @event show
18139          * Fires when this field hide.
18140          * @param {Roo.bootstrap.MonthField} this
18141          * @param {Mixed} date The date value
18142          */
18143         hide : true,
18144         /**
18145          * @event select
18146          * Fires when select a date.
18147          * @param {Roo.bootstrap.MonthField} this
18148          * @param {String} oldvalue The old value
18149          * @param {String} newvalue The new value
18150          */
18151         select : true
18152     });
18153 };
18154
18155 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18156     
18157     onRender: function(ct, position)
18158     {
18159         
18160         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18161         
18162         this.language = this.language || 'en';
18163         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18164         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18165         
18166         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18167         this.isInline = false;
18168         this.isInput = true;
18169         this.component = this.el.select('.add-on', true).first() || false;
18170         this.component = (this.component && this.component.length === 0) ? false : this.component;
18171         this.hasInput = this.component && this.inputEL().length;
18172         
18173         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18174         
18175         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18176         
18177         this.picker().on('mousedown', this.onMousedown, this);
18178         this.picker().on('click', this.onClick, this);
18179         
18180         this.picker().addClass('datepicker-dropdown');
18181         
18182         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18183             v.setStyle('width', '189px');
18184         });
18185         
18186         this.fillMonths();
18187         
18188         this.update();
18189         
18190         if(this.isInline) {
18191             this.show();
18192         }
18193         
18194     },
18195     
18196     setValue: function(v, suppressEvent)
18197     {   
18198         var o = this.getValue();
18199         
18200         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18201         
18202         this.update();
18203
18204         if(suppressEvent !== true){
18205             this.fireEvent('select', this, o, v);
18206         }
18207         
18208     },
18209     
18210     getValue: function()
18211     {
18212         return this.value;
18213     },
18214     
18215     onClick: function(e) 
18216     {
18217         e.stopPropagation();
18218         e.preventDefault();
18219         
18220         var target = e.getTarget();
18221         
18222         if(target.nodeName.toLowerCase() === 'i'){
18223             target = Roo.get(target).dom.parentNode;
18224         }
18225         
18226         var nodeName = target.nodeName;
18227         var className = target.className;
18228         var html = target.innerHTML;
18229         
18230         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18231             return;
18232         }
18233         
18234         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18235         
18236         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18237         
18238         this.hide();
18239                         
18240     },
18241     
18242     picker : function()
18243     {
18244         return this.pickerEl;
18245     },
18246     
18247     fillMonths: function()
18248     {    
18249         var i = 0;
18250         var months = this.picker().select('>.datepicker-months td', true).first();
18251         
18252         months.dom.innerHTML = '';
18253         
18254         while (i < 12) {
18255             var month = {
18256                 tag: 'span',
18257                 cls: 'month',
18258                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18259             };
18260             
18261             months.createChild(month);
18262         }
18263         
18264     },
18265     
18266     update: function()
18267     {
18268         var _this = this;
18269         
18270         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18271             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18272         }
18273         
18274         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18275             e.removeClass('active');
18276             
18277             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18278                 e.addClass('active');
18279             }
18280         })
18281     },
18282     
18283     place: function()
18284     {
18285         if(this.isInline) {
18286             return;
18287         }
18288         
18289         this.picker().removeClass(['bottom', 'top']);
18290         
18291         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18292             /*
18293              * place to the top of element!
18294              *
18295              */
18296             
18297             this.picker().addClass('top');
18298             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18299             
18300             return;
18301         }
18302         
18303         this.picker().addClass('bottom');
18304         
18305         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18306     },
18307     
18308     onFocus : function()
18309     {
18310         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18311         this.show();
18312     },
18313     
18314     onBlur : function()
18315     {
18316         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18317         
18318         var d = this.inputEl().getValue();
18319         
18320         this.setValue(d);
18321                 
18322         this.hide();
18323     },
18324     
18325     show : function()
18326     {
18327         this.picker().show();
18328         this.picker().select('>.datepicker-months', true).first().show();
18329         this.update();
18330         this.place();
18331         
18332         this.fireEvent('show', this, this.date);
18333     },
18334     
18335     hide : function()
18336     {
18337         if(this.isInline) {
18338             return;
18339         }
18340         this.picker().hide();
18341         this.fireEvent('hide', this, this.date);
18342         
18343     },
18344     
18345     onMousedown: function(e)
18346     {
18347         e.stopPropagation();
18348         e.preventDefault();
18349     },
18350     
18351     keyup: function(e)
18352     {
18353         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18354         this.update();
18355     },
18356
18357     fireKey: function(e)
18358     {
18359         if (!this.picker().isVisible()){
18360             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18361                 this.show();
18362             }
18363             return;
18364         }
18365         
18366         var dir;
18367         
18368         switch(e.keyCode){
18369             case 27: // escape
18370                 this.hide();
18371                 e.preventDefault();
18372                 break;
18373             case 37: // left
18374             case 39: // right
18375                 dir = e.keyCode == 37 ? -1 : 1;
18376                 
18377                 this.vIndex = this.vIndex + dir;
18378                 
18379                 if(this.vIndex < 0){
18380                     this.vIndex = 0;
18381                 }
18382                 
18383                 if(this.vIndex > 11){
18384                     this.vIndex = 11;
18385                 }
18386                 
18387                 if(isNaN(this.vIndex)){
18388                     this.vIndex = 0;
18389                 }
18390                 
18391                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18392                 
18393                 break;
18394             case 38: // up
18395             case 40: // down
18396                 
18397                 dir = e.keyCode == 38 ? -1 : 1;
18398                 
18399                 this.vIndex = this.vIndex + dir * 4;
18400                 
18401                 if(this.vIndex < 0){
18402                     this.vIndex = 0;
18403                 }
18404                 
18405                 if(this.vIndex > 11){
18406                     this.vIndex = 11;
18407                 }
18408                 
18409                 if(isNaN(this.vIndex)){
18410                     this.vIndex = 0;
18411                 }
18412                 
18413                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18414                 break;
18415                 
18416             case 13: // enter
18417                 
18418                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18419                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18420                 }
18421                 
18422                 this.hide();
18423                 e.preventDefault();
18424                 break;
18425             case 9: // tab
18426                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18427                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18428                 }
18429                 this.hide();
18430                 break;
18431             case 16: // shift
18432             case 17: // ctrl
18433             case 18: // alt
18434                 break;
18435             default :
18436                 this.hide();
18437                 
18438         }
18439     },
18440     
18441     remove: function() 
18442     {
18443         this.picker().remove();
18444     }
18445    
18446 });
18447
18448 Roo.apply(Roo.bootstrap.MonthField,  {
18449     
18450     content : {
18451         tag: 'tbody',
18452         cn: [
18453         {
18454             tag: 'tr',
18455             cn: [
18456             {
18457                 tag: 'td',
18458                 colspan: '7'
18459             }
18460             ]
18461         }
18462         ]
18463     },
18464     
18465     dates:{
18466         en: {
18467             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18468             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18469         }
18470     }
18471 });
18472
18473 Roo.apply(Roo.bootstrap.MonthField,  {
18474   
18475     template : {
18476         tag: 'div',
18477         cls: 'datepicker dropdown-menu roo-dynamic',
18478         cn: [
18479             {
18480                 tag: 'div',
18481                 cls: 'datepicker-months',
18482                 cn: [
18483                 {
18484                     tag: 'table',
18485                     cls: 'table-condensed',
18486                     cn:[
18487                         Roo.bootstrap.DateField.content
18488                     ]
18489                 }
18490                 ]
18491             }
18492         ]
18493     }
18494 });
18495
18496  
18497
18498  
18499  /*
18500  * - LGPL
18501  *
18502  * CheckBox
18503  * 
18504  */
18505
18506 /**
18507  * @class Roo.bootstrap.CheckBox
18508  * @extends Roo.bootstrap.Input
18509  * Bootstrap CheckBox class
18510  * 
18511  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18512  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18513  * @cfg {String} boxLabel The text that appears beside the checkbox
18514  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18515  * @cfg {Boolean} checked initnal the element
18516  * @cfg {Boolean} inline inline the element (default false)
18517  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18518  * 
18519  * @constructor
18520  * Create a new CheckBox
18521  * @param {Object} config The config object
18522  */
18523
18524 Roo.bootstrap.CheckBox = function(config){
18525     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18526    
18527     this.addEvents({
18528         /**
18529         * @event check
18530         * Fires when the element is checked or unchecked.
18531         * @param {Roo.bootstrap.CheckBox} this This input
18532         * @param {Boolean} checked The new checked value
18533         */
18534        check : true
18535     });
18536     
18537 };
18538
18539 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18540   
18541     inputType: 'checkbox',
18542     inputValue: 1,
18543     valueOff: 0,
18544     boxLabel: false,
18545     checked: false,
18546     weight : false,
18547     inline: false,
18548     
18549     getAutoCreate : function()
18550     {
18551         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18552         
18553         var id = Roo.id();
18554         
18555         var cfg = {};
18556         
18557         cfg.cls = 'form-group ' + this.inputType; //input-group
18558         
18559         if(this.inline){
18560             cfg.cls += ' ' + this.inputType + '-inline';
18561         }
18562         
18563         var input =  {
18564             tag: 'input',
18565             id : id,
18566             type : this.inputType,
18567             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18568             cls : 'roo-' + this.inputType, //'form-box',
18569             placeholder : this.placeholder || ''
18570             
18571         };
18572         
18573         if (this.weight) { // Validity check?
18574             cfg.cls += " " + this.inputType + "-" + this.weight;
18575         }
18576         
18577         if (this.disabled) {
18578             input.disabled=true;
18579         }
18580         
18581         if(this.checked){
18582             input.checked = this.checked;
18583         }
18584         
18585         if (this.name) {
18586             input.name = this.name;
18587         }
18588         
18589         if (this.size) {
18590             input.cls += ' input-' + this.size;
18591         }
18592         
18593         var settings=this;
18594         
18595         ['xs','sm','md','lg'].map(function(size){
18596             if (settings[size]) {
18597                 cfg.cls += ' col-' + size + '-' + settings[size];
18598             }
18599         });
18600         
18601         var inputblock = input;
18602          
18603         if (this.before || this.after) {
18604             
18605             inputblock = {
18606                 cls : 'input-group',
18607                 cn :  [] 
18608             };
18609             
18610             if (this.before) {
18611                 inputblock.cn.push({
18612                     tag :'span',
18613                     cls : 'input-group-addon',
18614                     html : this.before
18615                 });
18616             }
18617             
18618             inputblock.cn.push(input);
18619             
18620             if (this.after) {
18621                 inputblock.cn.push({
18622                     tag :'span',
18623                     cls : 'input-group-addon',
18624                     html : this.after
18625                 });
18626             }
18627             
18628         }
18629         
18630         if (align ==='left' && this.fieldLabel.length) {
18631 //                Roo.log("left and has label");
18632                 cfg.cn = [
18633                     
18634                     {
18635                         tag: 'label',
18636                         'for' :  id,
18637                         cls : 'control-label col-md-' + this.labelWidth,
18638                         html : this.fieldLabel
18639                         
18640                     },
18641                     {
18642                         cls : "col-md-" + (12 - this.labelWidth), 
18643                         cn: [
18644                             inputblock
18645                         ]
18646                     }
18647                     
18648                 ];
18649         } else if ( this.fieldLabel.length) {
18650 //                Roo.log(" label");
18651                 cfg.cn = [
18652                    
18653                     {
18654                         tag: this.boxLabel ? 'span' : 'label',
18655                         'for': id,
18656                         cls: 'control-label box-input-label',
18657                         //cls : 'input-group-addon',
18658                         html : this.fieldLabel
18659                         
18660                     },
18661                     
18662                     inputblock
18663                     
18664                 ];
18665
18666         } else {
18667             
18668 //                Roo.log(" no label && no align");
18669                 cfg.cn = [  inputblock ] ;
18670                 
18671                 
18672         }
18673         if(this.boxLabel){
18674              var boxLabelCfg = {
18675                 tag: 'label',
18676                 //'for': id, // box label is handled by onclick - so no for...
18677                 cls: 'box-label',
18678                 html: this.boxLabel
18679             };
18680             
18681             if(this.tooltip){
18682                 boxLabelCfg.tooltip = this.tooltip;
18683             }
18684              
18685             cfg.cn.push(boxLabelCfg);
18686         }
18687         
18688         
18689        
18690         return cfg;
18691         
18692     },
18693     
18694     /**
18695      * return the real input element.
18696      */
18697     inputEl: function ()
18698     {
18699         return this.el.select('input.roo-' + this.inputType,true).first();
18700     },
18701     
18702     labelEl: function()
18703     {
18704         return this.el.select('label.control-label',true).first();
18705     },
18706     /* depricated... */
18707     
18708     label: function()
18709     {
18710         return this.labelEl();
18711     },
18712     
18713     initEvents : function()
18714     {
18715 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18716         
18717         this.inputEl().on('click', this.onClick,  this);
18718         
18719         if (this.boxLabel) { 
18720             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18721         }
18722         
18723         this.startValue = this.getValue();
18724         
18725         if(this.groupId){
18726             Roo.bootstrap.CheckBox.register(this);
18727         }
18728     },
18729     
18730     onClick : function()
18731     {   
18732         this.setChecked(!this.checked);
18733     },
18734     
18735     setChecked : function(state,suppressEvent)
18736     {
18737         this.startValue = this.getValue();
18738         
18739         if(this.inputType == 'radio'){
18740             
18741             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18742                 e.dom.checked = false;
18743             });
18744             
18745             this.inputEl().dom.checked = true;
18746             
18747             this.inputEl().dom.value = this.inputValue;
18748             
18749             if(suppressEvent !== true){
18750                 this.fireEvent('check', this, true);
18751             }
18752             
18753             this.validate();
18754             
18755             return;
18756         }
18757         
18758         this.checked = state;
18759         
18760         this.inputEl().dom.checked = state;
18761         
18762         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18763         
18764         if(suppressEvent !== true){
18765             this.fireEvent('check', this, state);
18766         }
18767         
18768         this.validate();
18769     },
18770     
18771     getValue : function()
18772     {
18773         if(this.inputType == 'radio'){
18774             return this.getGroupValue();
18775         }
18776         
18777         return this.inputEl().getValue();
18778         
18779     },
18780     
18781     getGroupValue : function()
18782     {
18783         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18784             return '';
18785         }
18786         
18787         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18788     },
18789     
18790     setValue : function(v,suppressEvent)
18791     {
18792         if(this.inputType == 'radio'){
18793             this.setGroupValue(v, suppressEvent);
18794             return;
18795         }
18796         
18797         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18798         
18799         this.validate();
18800     },
18801     
18802     setGroupValue : function(v, suppressEvent)
18803     {
18804         this.startValue = this.getValue();
18805         
18806         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18807             e.dom.checked = false;
18808             
18809             if(e.dom.value == v){
18810                 e.dom.checked = true;
18811             }
18812         });
18813         
18814         if(suppressEvent !== true){
18815             this.fireEvent('check', this, true);
18816         }
18817
18818         this.validate();
18819         
18820         return;
18821     },
18822     
18823     validate : function()
18824     {
18825         if(
18826                 this.disabled || 
18827                 (this.inputType == 'radio' && this.validateRadio()) ||
18828                 (this.inputType == 'checkbox' && this.validateCheckbox())
18829         ){
18830             this.markValid();
18831             return true;
18832         }
18833         
18834         this.markInvalid();
18835         return false;
18836     },
18837     
18838     validateRadio : function()
18839     {
18840         var valid = false;
18841         
18842         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18843             if(!e.dom.checked){
18844                 return;
18845             }
18846             
18847             valid = true;
18848             
18849             return false;
18850         });
18851         
18852         return valid;
18853     },
18854     
18855     validateCheckbox : function()
18856     {
18857         if(!this.groupId){
18858             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18859         }
18860         
18861         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18862         
18863         if(!group){
18864             return false;
18865         }
18866         
18867         var r = false;
18868         
18869         for(var i in group){
18870             if(r){
18871                 break;
18872             }
18873             
18874             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18875         }
18876         
18877         return r;
18878     },
18879     
18880     /**
18881      * Mark this field as valid
18882      */
18883     markValid : function()
18884     {
18885         if(this.allowBlank){
18886             return;
18887         }
18888         
18889         var _this = this;
18890         
18891         this.fireEvent('valid', this);
18892         
18893         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18894         
18895         if(this.groupId){
18896             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18897         }
18898         
18899         if(label){
18900             label.markValid();
18901         }
18902         
18903         if(this.inputType == 'radio'){
18904             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18905                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18906                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18907             });
18908             
18909             return;
18910         }
18911         
18912         if(!this.groupId){
18913             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18914             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18915             return;
18916         }
18917         
18918         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18919             
18920         if(!group){
18921             return;
18922         }
18923         
18924         for(var i in group){
18925             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18926             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
18927         }
18928     },
18929     
18930      /**
18931      * Mark this field as invalid
18932      * @param {String} msg The validation message
18933      */
18934     markInvalid : function(msg)
18935     {
18936         if(this.allowBlank){
18937             return;
18938         }
18939         
18940         var _this = this;
18941         
18942         this.fireEvent('invalid', this, msg);
18943         
18944         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18945         
18946         if(this.groupId){
18947             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18948         }
18949         
18950         if(label){
18951             label.markInvalid();
18952         }
18953             
18954         if(this.inputType == 'radio'){
18955             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18956                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18957                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
18958             });
18959             
18960             return;
18961         }
18962         
18963         if(!this.groupId){
18964             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18965             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
18966             return;
18967         }
18968         
18969         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18970         
18971         if(!group){
18972             return;
18973         }
18974         
18975         for(var i in group){
18976             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18977             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
18978         }
18979         
18980     }
18981     
18982 });
18983
18984 Roo.apply(Roo.bootstrap.CheckBox, {
18985     
18986     groups: {},
18987     
18988      /**
18989     * register a CheckBox Group
18990     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
18991     */
18992     register : function(checkbox)
18993     {
18994         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
18995             this.groups[checkbox.groupId] = {};
18996         }
18997         
18998         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
18999             return;
19000         }
19001         
19002         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19003         
19004     },
19005     /**
19006     * fetch a CheckBox Group based on the group ID
19007     * @param {string} the group ID
19008     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19009     */
19010     get: function(groupId) {
19011         if (typeof(this.groups[groupId]) == 'undefined') {
19012             return false;
19013         }
19014         
19015         return this.groups[groupId] ;
19016     }
19017     
19018     
19019 });
19020 /*
19021  * - LGPL
19022  *
19023  * Radio
19024  *
19025  *
19026  * not inline
19027  *<div class="radio">
19028   <label>
19029     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19030     Option one is this and that&mdash;be sure to include why it's great
19031   </label>
19032 </div>
19033  *
19034  *
19035  *inline
19036  *<span>
19037  *<label class="radio-inline">fieldLabel</label>
19038  *<label class="radio-inline">
19039   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19040 </label>
19041 <span>
19042  * 
19043  * 
19044  */
19045
19046 /**
19047  * @class Roo.bootstrap.Radio
19048  * @extends Roo.bootstrap.CheckBox
19049  * Bootstrap Radio class
19050
19051  * @constructor
19052  * Create a new Radio
19053  * @param {Object} config The config object
19054  */
19055
19056 Roo.bootstrap.Radio = function(config){
19057     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19058    
19059 };
19060
19061 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19062     
19063     inputType: 'radio',
19064     inputValue: '',
19065     valueOff: '',
19066     
19067     getAutoCreate : function()
19068     {
19069         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19070         align = align || 'left'; // default...
19071         
19072         
19073         
19074         var id = Roo.id();
19075         
19076         var cfg = {
19077                 tag : this.inline ? 'span' : 'div',
19078                 cls : '',
19079                 cn : []
19080         };
19081         
19082         var inline = this.inline ? ' radio-inline' : '';
19083         
19084         var lbl = {
19085                 tag: 'label' ,
19086                 // does not need for, as we wrap the input with it..
19087                 'for' : id,
19088                 cls : 'control-label box-label' + inline,
19089                 cn : []
19090         };
19091         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19092         
19093         var fieldLabel = {
19094             tag: 'label' ,
19095             //cls : 'control-label' + inline,
19096             html : this.fieldLabel,
19097             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19098         };
19099         
19100  
19101         
19102         
19103         var input =  {
19104             tag: 'input',
19105             id : id,
19106             type : this.inputType,
19107             //value : (!this.checked) ? this.valueOff : this.inputValue,
19108             value : this.inputValue,
19109             cls : 'roo-radio',
19110             placeholder : this.placeholder || '' // ?? needed????
19111             
19112         };
19113         if (this.weight) { // Validity check?
19114             input.cls += " radio-" + this.weight;
19115         }
19116         if (this.disabled) {
19117             input.disabled=true;
19118         }
19119         
19120         if(this.checked){
19121             input.checked = this.checked;
19122         }
19123         
19124         if (this.name) {
19125             input.name = this.name;
19126         }
19127         
19128         if (this.size) {
19129             input.cls += ' input-' + this.size;
19130         }
19131         
19132         //?? can span's inline have a width??
19133         
19134         var settings=this;
19135         ['xs','sm','md','lg'].map(function(size){
19136             if (settings[size]) {
19137                 cfg.cls += ' col-' + size + '-' + settings[size];
19138             }
19139         });
19140         
19141         var inputblock = input;
19142         
19143         if (this.before || this.after) {
19144             
19145             inputblock = {
19146                 cls : 'input-group',
19147                 tag : 'span',
19148                 cn :  [] 
19149             };
19150             if (this.before) {
19151                 inputblock.cn.push({
19152                     tag :'span',
19153                     cls : 'input-group-addon',
19154                     html : this.before
19155                 });
19156             }
19157             inputblock.cn.push(input);
19158             if (this.after) {
19159                 inputblock.cn.push({
19160                     tag :'span',
19161                     cls : 'input-group-addon',
19162                     html : this.after
19163                 });
19164             }
19165             
19166         };
19167         
19168         
19169         if (this.fieldLabel && this.fieldLabel.length) {
19170             cfg.cn.push(fieldLabel);
19171         }
19172        
19173         // normal bootstrap puts the input inside the label.
19174         // however with our styled version - it has to go after the input.
19175        
19176         //lbl.cn.push(inputblock);
19177         
19178         var lblwrap =  {
19179             tag: 'span',
19180             cls: 'radio' + inline,
19181             cn: [
19182                 inputblock,
19183                 lbl
19184             ]
19185         };
19186         
19187         cfg.cn.push( lblwrap);
19188         
19189         if(this.boxLabel){
19190             lbl.cn.push({
19191                 tag: 'span',
19192                 html: this.boxLabel
19193             })
19194         }
19195          
19196         
19197         return cfg;
19198         
19199     },
19200     
19201     initEvents : function()
19202     {
19203 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19204         
19205         this.inputEl().on('click', this.onClick,  this);
19206         if (this.boxLabel) {
19207             //Roo.log('find label');
19208             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19209         }
19210         
19211     },
19212     
19213     inputEl: function ()
19214     {
19215         return this.el.select('input.roo-radio',true).first();
19216     },
19217     onClick : function()
19218     {   
19219         Roo.log("click");
19220         this.setChecked(true);
19221     },
19222     
19223     setChecked : function(state,suppressEvent)
19224     {
19225         if(state){
19226             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19227                 v.dom.checked = false;
19228             });
19229         }
19230         Roo.log(this.inputEl().dom);
19231         this.checked = state;
19232         this.inputEl().dom.checked = state;
19233         
19234         if(suppressEvent !== true){
19235             this.fireEvent('check', this, state);
19236         }
19237         
19238         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19239         
19240     },
19241     
19242     getGroupValue : function()
19243     {
19244         var value = '';
19245         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19246             if(v.dom.checked == true){
19247                 value = v.dom.value;
19248             }
19249         });
19250         
19251         return value;
19252     },
19253     
19254     /**
19255      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19256      * @return {Mixed} value The field value
19257      */
19258     getValue : function(){
19259         return this.getGroupValue();
19260     }
19261     
19262 });
19263
19264  
19265 //<script type="text/javascript">
19266
19267 /*
19268  * Based  Ext JS Library 1.1.1
19269  * Copyright(c) 2006-2007, Ext JS, LLC.
19270  * LGPL
19271  *
19272  */
19273  
19274 /**
19275  * @class Roo.HtmlEditorCore
19276  * @extends Roo.Component
19277  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19278  *
19279  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19280  */
19281
19282 Roo.HtmlEditorCore = function(config){
19283     
19284     
19285     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19286     
19287     
19288     this.addEvents({
19289         /**
19290          * @event initialize
19291          * Fires when the editor is fully initialized (including the iframe)
19292          * @param {Roo.HtmlEditorCore} this
19293          */
19294         initialize: true,
19295         /**
19296          * @event activate
19297          * Fires when the editor is first receives the focus. Any insertion must wait
19298          * until after this event.
19299          * @param {Roo.HtmlEditorCore} this
19300          */
19301         activate: true,
19302          /**
19303          * @event beforesync
19304          * Fires before the textarea is updated with content from the editor iframe. Return false
19305          * to cancel the sync.
19306          * @param {Roo.HtmlEditorCore} this
19307          * @param {String} html
19308          */
19309         beforesync: true,
19310          /**
19311          * @event beforepush
19312          * Fires before the iframe editor is updated with content from the textarea. Return false
19313          * to cancel the push.
19314          * @param {Roo.HtmlEditorCore} this
19315          * @param {String} html
19316          */
19317         beforepush: true,
19318          /**
19319          * @event sync
19320          * Fires when the textarea is updated with content from the editor iframe.
19321          * @param {Roo.HtmlEditorCore} this
19322          * @param {String} html
19323          */
19324         sync: true,
19325          /**
19326          * @event push
19327          * Fires when the iframe editor is updated with content from the textarea.
19328          * @param {Roo.HtmlEditorCore} this
19329          * @param {String} html
19330          */
19331         push: true,
19332         
19333         /**
19334          * @event editorevent
19335          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19336          * @param {Roo.HtmlEditorCore} this
19337          */
19338         editorevent: true
19339         
19340     });
19341     
19342     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19343     
19344     // defaults : white / black...
19345     this.applyBlacklists();
19346     
19347     
19348     
19349 };
19350
19351
19352 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19353
19354
19355      /**
19356      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19357      */
19358     
19359     owner : false,
19360     
19361      /**
19362      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19363      *                        Roo.resizable.
19364      */
19365     resizable : false,
19366      /**
19367      * @cfg {Number} height (in pixels)
19368      */   
19369     height: 300,
19370    /**
19371      * @cfg {Number} width (in pixels)
19372      */   
19373     width: 500,
19374     
19375     /**
19376      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19377      * 
19378      */
19379     stylesheets: false,
19380     
19381     // id of frame..
19382     frameId: false,
19383     
19384     // private properties
19385     validationEvent : false,
19386     deferHeight: true,
19387     initialized : false,
19388     activated : false,
19389     sourceEditMode : false,
19390     onFocus : Roo.emptyFn,
19391     iframePad:3,
19392     hideMode:'offsets',
19393     
19394     clearUp: true,
19395     
19396     // blacklist + whitelisted elements..
19397     black: false,
19398     white: false,
19399      
19400     
19401
19402     /**
19403      * Protected method that will not generally be called directly. It
19404      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19405      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19406      */
19407     getDocMarkup : function(){
19408         // body styles..
19409         var st = '';
19410         
19411         // inherit styels from page...?? 
19412         if (this.stylesheets === false) {
19413             
19414             Roo.get(document.head).select('style').each(function(node) {
19415                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19416             });
19417             
19418             Roo.get(document.head).select('link').each(function(node) { 
19419                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19420             });
19421             
19422         } else if (!this.stylesheets.length) {
19423                 // simple..
19424                 st = '<style type="text/css">' +
19425                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19426                    '</style>';
19427         } else { 
19428             
19429         }
19430         
19431         st +=  '<style type="text/css">' +
19432             'IMG { cursor: pointer } ' +
19433         '</style>';
19434
19435         
19436         return '<html><head>' + st  +
19437             //<style type="text/css">' +
19438             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19439             //'</style>' +
19440             ' </head><body class="roo-htmleditor-body"></body></html>';
19441     },
19442
19443     // private
19444     onRender : function(ct, position)
19445     {
19446         var _t = this;
19447         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19448         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19449         
19450         
19451         this.el.dom.style.border = '0 none';
19452         this.el.dom.setAttribute('tabIndex', -1);
19453         this.el.addClass('x-hidden hide');
19454         
19455         
19456         
19457         if(Roo.isIE){ // fix IE 1px bogus margin
19458             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19459         }
19460        
19461         
19462         this.frameId = Roo.id();
19463         
19464          
19465         
19466         var iframe = this.owner.wrap.createChild({
19467             tag: 'iframe',
19468             cls: 'form-control', // bootstrap..
19469             id: this.frameId,
19470             name: this.frameId,
19471             frameBorder : 'no',
19472             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19473         }, this.el
19474         );
19475         
19476         
19477         this.iframe = iframe.dom;
19478
19479          this.assignDocWin();
19480         
19481         this.doc.designMode = 'on';
19482        
19483         this.doc.open();
19484         this.doc.write(this.getDocMarkup());
19485         this.doc.close();
19486
19487         
19488         var task = { // must defer to wait for browser to be ready
19489             run : function(){
19490                 //console.log("run task?" + this.doc.readyState);
19491                 this.assignDocWin();
19492                 if(this.doc.body || this.doc.readyState == 'complete'){
19493                     try {
19494                         this.doc.designMode="on";
19495                     } catch (e) {
19496                         return;
19497                     }
19498                     Roo.TaskMgr.stop(task);
19499                     this.initEditor.defer(10, this);
19500                 }
19501             },
19502             interval : 10,
19503             duration: 10000,
19504             scope: this
19505         };
19506         Roo.TaskMgr.start(task);
19507
19508     },
19509
19510     // private
19511     onResize : function(w, h)
19512     {
19513          Roo.log('resize: ' +w + ',' + h );
19514         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19515         if(!this.iframe){
19516             return;
19517         }
19518         if(typeof w == 'number'){
19519             
19520             this.iframe.style.width = w + 'px';
19521         }
19522         if(typeof h == 'number'){
19523             
19524             this.iframe.style.height = h + 'px';
19525             if(this.doc){
19526                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19527             }
19528         }
19529         
19530     },
19531
19532     /**
19533      * Toggles the editor between standard and source edit mode.
19534      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19535      */
19536     toggleSourceEdit : function(sourceEditMode){
19537         
19538         this.sourceEditMode = sourceEditMode === true;
19539         
19540         if(this.sourceEditMode){
19541  
19542             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19543             
19544         }else{
19545             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19546             //this.iframe.className = '';
19547             this.deferFocus();
19548         }
19549         //this.setSize(this.owner.wrap.getSize());
19550         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19551     },
19552
19553     
19554   
19555
19556     /**
19557      * Protected method that will not generally be called directly. If you need/want
19558      * custom HTML cleanup, this is the method you should override.
19559      * @param {String} html The HTML to be cleaned
19560      * return {String} The cleaned HTML
19561      */
19562     cleanHtml : function(html){
19563         html = String(html);
19564         if(html.length > 5){
19565             if(Roo.isSafari){ // strip safari nonsense
19566                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19567             }
19568         }
19569         if(html == '&nbsp;'){
19570             html = '';
19571         }
19572         return html;
19573     },
19574
19575     /**
19576      * HTML Editor -> Textarea
19577      * Protected method that will not generally be called directly. Syncs the contents
19578      * of the editor iframe with the textarea.
19579      */
19580     syncValue : function(){
19581         if(this.initialized){
19582             var bd = (this.doc.body || this.doc.documentElement);
19583             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19584             var html = bd.innerHTML;
19585             if(Roo.isSafari){
19586                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19587                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19588                 if(m && m[1]){
19589                     html = '<div style="'+m[0]+'">' + html + '</div>';
19590                 }
19591             }
19592             html = this.cleanHtml(html);
19593             // fix up the special chars.. normaly like back quotes in word...
19594             // however we do not want to do this with chinese..
19595             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19596                 var cc = b.charCodeAt();
19597                 if (
19598                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19599                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19600                     (cc >= 0xf900 && cc < 0xfb00 )
19601                 ) {
19602                         return b;
19603                 }
19604                 return "&#"+cc+";" 
19605             });
19606             if(this.owner.fireEvent('beforesync', this, html) !== false){
19607                 this.el.dom.value = html;
19608                 this.owner.fireEvent('sync', this, html);
19609             }
19610         }
19611     },
19612
19613     /**
19614      * Protected method that will not generally be called directly. Pushes the value of the textarea
19615      * into the iframe editor.
19616      */
19617     pushValue : function(){
19618         if(this.initialized){
19619             var v = this.el.dom.value.trim();
19620             
19621 //            if(v.length < 1){
19622 //                v = '&#160;';
19623 //            }
19624             
19625             if(this.owner.fireEvent('beforepush', this, v) !== false){
19626                 var d = (this.doc.body || this.doc.documentElement);
19627                 d.innerHTML = v;
19628                 this.cleanUpPaste();
19629                 this.el.dom.value = d.innerHTML;
19630                 this.owner.fireEvent('push', this, v);
19631             }
19632         }
19633     },
19634
19635     // private
19636     deferFocus : function(){
19637         this.focus.defer(10, this);
19638     },
19639
19640     // doc'ed in Field
19641     focus : function(){
19642         if(this.win && !this.sourceEditMode){
19643             this.win.focus();
19644         }else{
19645             this.el.focus();
19646         }
19647     },
19648     
19649     assignDocWin: function()
19650     {
19651         var iframe = this.iframe;
19652         
19653          if(Roo.isIE){
19654             this.doc = iframe.contentWindow.document;
19655             this.win = iframe.contentWindow;
19656         } else {
19657 //            if (!Roo.get(this.frameId)) {
19658 //                return;
19659 //            }
19660 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19661 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19662             
19663             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19664                 return;
19665             }
19666             
19667             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19668             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19669         }
19670     },
19671     
19672     // private
19673     initEditor : function(){
19674         //console.log("INIT EDITOR");
19675         this.assignDocWin();
19676         
19677         
19678         
19679         this.doc.designMode="on";
19680         this.doc.open();
19681         this.doc.write(this.getDocMarkup());
19682         this.doc.close();
19683         
19684         var dbody = (this.doc.body || this.doc.documentElement);
19685         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19686         // this copies styles from the containing element into thsi one..
19687         // not sure why we need all of this..
19688         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19689         
19690         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19691         //ss['background-attachment'] = 'fixed'; // w3c
19692         dbody.bgProperties = 'fixed'; // ie
19693         //Roo.DomHelper.applyStyles(dbody, ss);
19694         Roo.EventManager.on(this.doc, {
19695             //'mousedown': this.onEditorEvent,
19696             'mouseup': this.onEditorEvent,
19697             'dblclick': this.onEditorEvent,
19698             'click': this.onEditorEvent,
19699             'keyup': this.onEditorEvent,
19700             buffer:100,
19701             scope: this
19702         });
19703         if(Roo.isGecko){
19704             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19705         }
19706         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19707             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19708         }
19709         this.initialized = true;
19710
19711         this.owner.fireEvent('initialize', this);
19712         this.pushValue();
19713     },
19714
19715     // private
19716     onDestroy : function(){
19717         
19718         
19719         
19720         if(this.rendered){
19721             
19722             //for (var i =0; i < this.toolbars.length;i++) {
19723             //    // fixme - ask toolbars for heights?
19724             //    this.toolbars[i].onDestroy();
19725            // }
19726             
19727             //this.wrap.dom.innerHTML = '';
19728             //this.wrap.remove();
19729         }
19730     },
19731
19732     // private
19733     onFirstFocus : function(){
19734         
19735         this.assignDocWin();
19736         
19737         
19738         this.activated = true;
19739          
19740     
19741         if(Roo.isGecko){ // prevent silly gecko errors
19742             this.win.focus();
19743             var s = this.win.getSelection();
19744             if(!s.focusNode || s.focusNode.nodeType != 3){
19745                 var r = s.getRangeAt(0);
19746                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19747                 r.collapse(true);
19748                 this.deferFocus();
19749             }
19750             try{
19751                 this.execCmd('useCSS', true);
19752                 this.execCmd('styleWithCSS', false);
19753             }catch(e){}
19754         }
19755         this.owner.fireEvent('activate', this);
19756     },
19757
19758     // private
19759     adjustFont: function(btn){
19760         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19761         //if(Roo.isSafari){ // safari
19762         //    adjust *= 2;
19763        // }
19764         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19765         if(Roo.isSafari){ // safari
19766             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19767             v =  (v < 10) ? 10 : v;
19768             v =  (v > 48) ? 48 : v;
19769             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19770             
19771         }
19772         
19773         
19774         v = Math.max(1, v+adjust);
19775         
19776         this.execCmd('FontSize', v  );
19777     },
19778
19779     onEditorEvent : function(e)
19780     {
19781         this.owner.fireEvent('editorevent', this, e);
19782       //  this.updateToolbar();
19783         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19784     },
19785
19786     insertTag : function(tg)
19787     {
19788         // could be a bit smarter... -> wrap the current selected tRoo..
19789         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19790             
19791             range = this.createRange(this.getSelection());
19792             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19793             wrappingNode.appendChild(range.extractContents());
19794             range.insertNode(wrappingNode);
19795
19796             return;
19797             
19798             
19799             
19800         }
19801         this.execCmd("formatblock",   tg);
19802         
19803     },
19804     
19805     insertText : function(txt)
19806     {
19807         
19808         
19809         var range = this.createRange();
19810         range.deleteContents();
19811                //alert(Sender.getAttribute('label'));
19812                
19813         range.insertNode(this.doc.createTextNode(txt));
19814     } ,
19815     
19816      
19817
19818     /**
19819      * Executes a Midas editor command on the editor document and performs necessary focus and
19820      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19821      * @param {String} cmd The Midas command
19822      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19823      */
19824     relayCmd : function(cmd, value){
19825         this.win.focus();
19826         this.execCmd(cmd, value);
19827         this.owner.fireEvent('editorevent', this);
19828         //this.updateToolbar();
19829         this.owner.deferFocus();
19830     },
19831
19832     /**
19833      * Executes a Midas editor command directly on the editor document.
19834      * For visual commands, you should use {@link #relayCmd} instead.
19835      * <b>This should only be called after the editor is initialized.</b>
19836      * @param {String} cmd The Midas command
19837      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19838      */
19839     execCmd : function(cmd, value){
19840         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19841         this.syncValue();
19842     },
19843  
19844  
19845    
19846     /**
19847      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19848      * to insert tRoo.
19849      * @param {String} text | dom node.. 
19850      */
19851     insertAtCursor : function(text)
19852     {
19853         
19854         
19855         
19856         if(!this.activated){
19857             return;
19858         }
19859         /*
19860         if(Roo.isIE){
19861             this.win.focus();
19862             var r = this.doc.selection.createRange();
19863             if(r){
19864                 r.collapse(true);
19865                 r.pasteHTML(text);
19866                 this.syncValue();
19867                 this.deferFocus();
19868             
19869             }
19870             return;
19871         }
19872         */
19873         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19874             this.win.focus();
19875             
19876             
19877             // from jquery ui (MIT licenced)
19878             var range, node;
19879             var win = this.win;
19880             
19881             if (win.getSelection && win.getSelection().getRangeAt) {
19882                 range = win.getSelection().getRangeAt(0);
19883                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19884                 range.insertNode(node);
19885             } else if (win.document.selection && win.document.selection.createRange) {
19886                 // no firefox support
19887                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19888                 win.document.selection.createRange().pasteHTML(txt);
19889             } else {
19890                 // no firefox support
19891                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19892                 this.execCmd('InsertHTML', txt);
19893             } 
19894             
19895             this.syncValue();
19896             
19897             this.deferFocus();
19898         }
19899     },
19900  // private
19901     mozKeyPress : function(e){
19902         if(e.ctrlKey){
19903             var c = e.getCharCode(), cmd;
19904           
19905             if(c > 0){
19906                 c = String.fromCharCode(c).toLowerCase();
19907                 switch(c){
19908                     case 'b':
19909                         cmd = 'bold';
19910                         break;
19911                     case 'i':
19912                         cmd = 'italic';
19913                         break;
19914                     
19915                     case 'u':
19916                         cmd = 'underline';
19917                         break;
19918                     
19919                     case 'v':
19920                         this.cleanUpPaste.defer(100, this);
19921                         return;
19922                         
19923                 }
19924                 if(cmd){
19925                     this.win.focus();
19926                     this.execCmd(cmd);
19927                     this.deferFocus();
19928                     e.preventDefault();
19929                 }
19930                 
19931             }
19932         }
19933     },
19934
19935     // private
19936     fixKeys : function(){ // load time branching for fastest keydown performance
19937         if(Roo.isIE){
19938             return function(e){
19939                 var k = e.getKey(), r;
19940                 if(k == e.TAB){
19941                     e.stopEvent();
19942                     r = this.doc.selection.createRange();
19943                     if(r){
19944                         r.collapse(true);
19945                         r.pasteHTML('&#160;&#160;&#160;&#160;');
19946                         this.deferFocus();
19947                     }
19948                     return;
19949                 }
19950                 
19951                 if(k == e.ENTER){
19952                     r = this.doc.selection.createRange();
19953                     if(r){
19954                         var target = r.parentElement();
19955                         if(!target || target.tagName.toLowerCase() != 'li'){
19956                             e.stopEvent();
19957                             r.pasteHTML('<br />');
19958                             r.collapse(false);
19959                             r.select();
19960                         }
19961                     }
19962                 }
19963                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19964                     this.cleanUpPaste.defer(100, this);
19965                     return;
19966                 }
19967                 
19968                 
19969             };
19970         }else if(Roo.isOpera){
19971             return function(e){
19972                 var k = e.getKey();
19973                 if(k == e.TAB){
19974                     e.stopEvent();
19975                     this.win.focus();
19976                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
19977                     this.deferFocus();
19978                 }
19979                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19980                     this.cleanUpPaste.defer(100, this);
19981                     return;
19982                 }
19983                 
19984             };
19985         }else if(Roo.isSafari){
19986             return function(e){
19987                 var k = e.getKey();
19988                 
19989                 if(k == e.TAB){
19990                     e.stopEvent();
19991                     this.execCmd('InsertText','\t');
19992                     this.deferFocus();
19993                     return;
19994                 }
19995                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
19996                     this.cleanUpPaste.defer(100, this);
19997                     return;
19998                 }
19999                 
20000              };
20001         }
20002     }(),
20003     
20004     getAllAncestors: function()
20005     {
20006         var p = this.getSelectedNode();
20007         var a = [];
20008         if (!p) {
20009             a.push(p); // push blank onto stack..
20010             p = this.getParentElement();
20011         }
20012         
20013         
20014         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20015             a.push(p);
20016             p = p.parentNode;
20017         }
20018         a.push(this.doc.body);
20019         return a;
20020     },
20021     lastSel : false,
20022     lastSelNode : false,
20023     
20024     
20025     getSelection : function() 
20026     {
20027         this.assignDocWin();
20028         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20029     },
20030     
20031     getSelectedNode: function() 
20032     {
20033         // this may only work on Gecko!!!
20034         
20035         // should we cache this!!!!
20036         
20037         
20038         
20039          
20040         var range = this.createRange(this.getSelection()).cloneRange();
20041         
20042         if (Roo.isIE) {
20043             var parent = range.parentElement();
20044             while (true) {
20045                 var testRange = range.duplicate();
20046                 testRange.moveToElementText(parent);
20047                 if (testRange.inRange(range)) {
20048                     break;
20049                 }
20050                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20051                     break;
20052                 }
20053                 parent = parent.parentElement;
20054             }
20055             return parent;
20056         }
20057         
20058         // is ancestor a text element.
20059         var ac =  range.commonAncestorContainer;
20060         if (ac.nodeType == 3) {
20061             ac = ac.parentNode;
20062         }
20063         
20064         var ar = ac.childNodes;
20065          
20066         var nodes = [];
20067         var other_nodes = [];
20068         var has_other_nodes = false;
20069         for (var i=0;i<ar.length;i++) {
20070             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20071                 continue;
20072             }
20073             // fullly contained node.
20074             
20075             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20076                 nodes.push(ar[i]);
20077                 continue;
20078             }
20079             
20080             // probably selected..
20081             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20082                 other_nodes.push(ar[i]);
20083                 continue;
20084             }
20085             // outer..
20086             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20087                 continue;
20088             }
20089             
20090             
20091             has_other_nodes = true;
20092         }
20093         if (!nodes.length && other_nodes.length) {
20094             nodes= other_nodes;
20095         }
20096         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20097             return false;
20098         }
20099         
20100         return nodes[0];
20101     },
20102     createRange: function(sel)
20103     {
20104         // this has strange effects when using with 
20105         // top toolbar - not sure if it's a great idea.
20106         //this.editor.contentWindow.focus();
20107         if (typeof sel != "undefined") {
20108             try {
20109                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20110             } catch(e) {
20111                 return this.doc.createRange();
20112             }
20113         } else {
20114             return this.doc.createRange();
20115         }
20116     },
20117     getParentElement: function()
20118     {
20119         
20120         this.assignDocWin();
20121         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20122         
20123         var range = this.createRange(sel);
20124          
20125         try {
20126             var p = range.commonAncestorContainer;
20127             while (p.nodeType == 3) { // text node
20128                 p = p.parentNode;
20129             }
20130             return p;
20131         } catch (e) {
20132             return null;
20133         }
20134     
20135     },
20136     /***
20137      *
20138      * Range intersection.. the hard stuff...
20139      *  '-1' = before
20140      *  '0' = hits..
20141      *  '1' = after.
20142      *         [ -- selected range --- ]
20143      *   [fail]                        [fail]
20144      *
20145      *    basically..
20146      *      if end is before start or  hits it. fail.
20147      *      if start is after end or hits it fail.
20148      *
20149      *   if either hits (but other is outside. - then it's not 
20150      *   
20151      *    
20152      **/
20153     
20154     
20155     // @see http://www.thismuchiknow.co.uk/?p=64.
20156     rangeIntersectsNode : function(range, node)
20157     {
20158         var nodeRange = node.ownerDocument.createRange();
20159         try {
20160             nodeRange.selectNode(node);
20161         } catch (e) {
20162             nodeRange.selectNodeContents(node);
20163         }
20164     
20165         var rangeStartRange = range.cloneRange();
20166         rangeStartRange.collapse(true);
20167     
20168         var rangeEndRange = range.cloneRange();
20169         rangeEndRange.collapse(false);
20170     
20171         var nodeStartRange = nodeRange.cloneRange();
20172         nodeStartRange.collapse(true);
20173     
20174         var nodeEndRange = nodeRange.cloneRange();
20175         nodeEndRange.collapse(false);
20176     
20177         return rangeStartRange.compareBoundaryPoints(
20178                  Range.START_TO_START, nodeEndRange) == -1 &&
20179                rangeEndRange.compareBoundaryPoints(
20180                  Range.START_TO_START, nodeStartRange) == 1;
20181         
20182          
20183     },
20184     rangeCompareNode : function(range, node)
20185     {
20186         var nodeRange = node.ownerDocument.createRange();
20187         try {
20188             nodeRange.selectNode(node);
20189         } catch (e) {
20190             nodeRange.selectNodeContents(node);
20191         }
20192         
20193         
20194         range.collapse(true);
20195     
20196         nodeRange.collapse(true);
20197      
20198         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20199         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20200          
20201         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20202         
20203         var nodeIsBefore   =  ss == 1;
20204         var nodeIsAfter    = ee == -1;
20205         
20206         if (nodeIsBefore && nodeIsAfter) {
20207             return 0; // outer
20208         }
20209         if (!nodeIsBefore && nodeIsAfter) {
20210             return 1; //right trailed.
20211         }
20212         
20213         if (nodeIsBefore && !nodeIsAfter) {
20214             return 2;  // left trailed.
20215         }
20216         // fully contined.
20217         return 3;
20218     },
20219
20220     // private? - in a new class?
20221     cleanUpPaste :  function()
20222     {
20223         // cleans up the whole document..
20224         Roo.log('cleanuppaste');
20225         
20226         this.cleanUpChildren(this.doc.body);
20227         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20228         if (clean != this.doc.body.innerHTML) {
20229             this.doc.body.innerHTML = clean;
20230         }
20231         
20232     },
20233     
20234     cleanWordChars : function(input) {// change the chars to hex code
20235         var he = Roo.HtmlEditorCore;
20236         
20237         var output = input;
20238         Roo.each(he.swapCodes, function(sw) { 
20239             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20240             
20241             output = output.replace(swapper, sw[1]);
20242         });
20243         
20244         return output;
20245     },
20246     
20247     
20248     cleanUpChildren : function (n)
20249     {
20250         if (!n.childNodes.length) {
20251             return;
20252         }
20253         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20254            this.cleanUpChild(n.childNodes[i]);
20255         }
20256     },
20257     
20258     
20259         
20260     
20261     cleanUpChild : function (node)
20262     {
20263         var ed = this;
20264         //console.log(node);
20265         if (node.nodeName == "#text") {
20266             // clean up silly Windows -- stuff?
20267             return; 
20268         }
20269         if (node.nodeName == "#comment") {
20270             node.parentNode.removeChild(node);
20271             // clean up silly Windows -- stuff?
20272             return; 
20273         }
20274         var lcname = node.tagName.toLowerCase();
20275         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20276         // whitelist of tags..
20277         
20278         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20279             // remove node.
20280             node.parentNode.removeChild(node);
20281             return;
20282             
20283         }
20284         
20285         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20286         
20287         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20288         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20289         
20290         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20291         //    remove_keep_children = true;
20292         //}
20293         
20294         if (remove_keep_children) {
20295             this.cleanUpChildren(node);
20296             // inserts everything just before this node...
20297             while (node.childNodes.length) {
20298                 var cn = node.childNodes[0];
20299                 node.removeChild(cn);
20300                 node.parentNode.insertBefore(cn, node);
20301             }
20302             node.parentNode.removeChild(node);
20303             return;
20304         }
20305         
20306         if (!node.attributes || !node.attributes.length) {
20307             this.cleanUpChildren(node);
20308             return;
20309         }
20310         
20311         function cleanAttr(n,v)
20312         {
20313             
20314             if (v.match(/^\./) || v.match(/^\//)) {
20315                 return;
20316             }
20317             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20318                 return;
20319             }
20320             if (v.match(/^#/)) {
20321                 return;
20322             }
20323 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20324             node.removeAttribute(n);
20325             
20326         }
20327         
20328         var cwhite = this.cwhite;
20329         var cblack = this.cblack;
20330             
20331         function cleanStyle(n,v)
20332         {
20333             if (v.match(/expression/)) { //XSS?? should we even bother..
20334                 node.removeAttribute(n);
20335                 return;
20336             }
20337             
20338             var parts = v.split(/;/);
20339             var clean = [];
20340             
20341             Roo.each(parts, function(p) {
20342                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20343                 if (!p.length) {
20344                     return true;
20345                 }
20346                 var l = p.split(':').shift().replace(/\s+/g,'');
20347                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20348                 
20349                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20350 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20351                     //node.removeAttribute(n);
20352                     return true;
20353                 }
20354                 //Roo.log()
20355                 // only allow 'c whitelisted system attributes'
20356                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20357 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20358                     //node.removeAttribute(n);
20359                     return true;
20360                 }
20361                 
20362                 
20363                  
20364                 
20365                 clean.push(p);
20366                 return true;
20367             });
20368             if (clean.length) { 
20369                 node.setAttribute(n, clean.join(';'));
20370             } else {
20371                 node.removeAttribute(n);
20372             }
20373             
20374         }
20375         
20376         
20377         for (var i = node.attributes.length-1; i > -1 ; i--) {
20378             var a = node.attributes[i];
20379             //console.log(a);
20380             
20381             if (a.name.toLowerCase().substr(0,2)=='on')  {
20382                 node.removeAttribute(a.name);
20383                 continue;
20384             }
20385             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20386                 node.removeAttribute(a.name);
20387                 continue;
20388             }
20389             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20390                 cleanAttr(a.name,a.value); // fixme..
20391                 continue;
20392             }
20393             if (a.name == 'style') {
20394                 cleanStyle(a.name,a.value);
20395                 continue;
20396             }
20397             /// clean up MS crap..
20398             // tecnically this should be a list of valid class'es..
20399             
20400             
20401             if (a.name == 'class') {
20402                 if (a.value.match(/^Mso/)) {
20403                     node.className = '';
20404                 }
20405                 
20406                 if (a.value.match(/body/)) {
20407                     node.className = '';
20408                 }
20409                 continue;
20410             }
20411             
20412             // style cleanup!?
20413             // class cleanup?
20414             
20415         }
20416         
20417         
20418         this.cleanUpChildren(node);
20419         
20420         
20421     },
20422     
20423     /**
20424      * Clean up MS wordisms...
20425      */
20426     cleanWord : function(node)
20427     {
20428         
20429         
20430         if (!node) {
20431             this.cleanWord(this.doc.body);
20432             return;
20433         }
20434         if (node.nodeName == "#text") {
20435             // clean up silly Windows -- stuff?
20436             return; 
20437         }
20438         if (node.nodeName == "#comment") {
20439             node.parentNode.removeChild(node);
20440             // clean up silly Windows -- stuff?
20441             return; 
20442         }
20443         
20444         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20445             node.parentNode.removeChild(node);
20446             return;
20447         }
20448         
20449         // remove - but keep children..
20450         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20451             while (node.childNodes.length) {
20452                 var cn = node.childNodes[0];
20453                 node.removeChild(cn);
20454                 node.parentNode.insertBefore(cn, node);
20455             }
20456             node.parentNode.removeChild(node);
20457             this.iterateChildren(node, this.cleanWord);
20458             return;
20459         }
20460         // clean styles
20461         if (node.className.length) {
20462             
20463             var cn = node.className.split(/\W+/);
20464             var cna = [];
20465             Roo.each(cn, function(cls) {
20466                 if (cls.match(/Mso[a-zA-Z]+/)) {
20467                     return;
20468                 }
20469                 cna.push(cls);
20470             });
20471             node.className = cna.length ? cna.join(' ') : '';
20472             if (!cna.length) {
20473                 node.removeAttribute("class");
20474             }
20475         }
20476         
20477         if (node.hasAttribute("lang")) {
20478             node.removeAttribute("lang");
20479         }
20480         
20481         if (node.hasAttribute("style")) {
20482             
20483             var styles = node.getAttribute("style").split(";");
20484             var nstyle = [];
20485             Roo.each(styles, function(s) {
20486                 if (!s.match(/:/)) {
20487                     return;
20488                 }
20489                 var kv = s.split(":");
20490                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20491                     return;
20492                 }
20493                 // what ever is left... we allow.
20494                 nstyle.push(s);
20495             });
20496             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20497             if (!nstyle.length) {
20498                 node.removeAttribute('style');
20499             }
20500         }
20501         this.iterateChildren(node, this.cleanWord);
20502         
20503         
20504         
20505     },
20506     /**
20507      * iterateChildren of a Node, calling fn each time, using this as the scole..
20508      * @param {DomNode} node node to iterate children of.
20509      * @param {Function} fn method of this class to call on each item.
20510      */
20511     iterateChildren : function(node, fn)
20512     {
20513         if (!node.childNodes.length) {
20514                 return;
20515         }
20516         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20517            fn.call(this, node.childNodes[i])
20518         }
20519     },
20520     
20521     
20522     /**
20523      * cleanTableWidths.
20524      *
20525      * Quite often pasting from word etc.. results in tables with column and widths.
20526      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20527      *
20528      */
20529     cleanTableWidths : function(node)
20530     {
20531          
20532          
20533         if (!node) {
20534             this.cleanTableWidths(this.doc.body);
20535             return;
20536         }
20537         
20538         // ignore list...
20539         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20540             return; 
20541         }
20542         Roo.log(node.tagName);
20543         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20544             this.iterateChildren(node, this.cleanTableWidths);
20545             return;
20546         }
20547         if (node.hasAttribute('width')) {
20548             node.removeAttribute('width');
20549         }
20550         
20551          
20552         if (node.hasAttribute("style")) {
20553             // pretty basic...
20554             
20555             var styles = node.getAttribute("style").split(";");
20556             var nstyle = [];
20557             Roo.each(styles, function(s) {
20558                 if (!s.match(/:/)) {
20559                     return;
20560                 }
20561                 var kv = s.split(":");
20562                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20563                     return;
20564                 }
20565                 // what ever is left... we allow.
20566                 nstyle.push(s);
20567             });
20568             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20569             if (!nstyle.length) {
20570                 node.removeAttribute('style');
20571             }
20572         }
20573         
20574         this.iterateChildren(node, this.cleanTableWidths);
20575         
20576         
20577     },
20578     
20579     
20580     
20581     
20582     domToHTML : function(currentElement, depth, nopadtext) {
20583         
20584         depth = depth || 0;
20585         nopadtext = nopadtext || false;
20586     
20587         if (!currentElement) {
20588             return this.domToHTML(this.doc.body);
20589         }
20590         
20591         //Roo.log(currentElement);
20592         var j;
20593         var allText = false;
20594         var nodeName = currentElement.nodeName;
20595         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20596         
20597         if  (nodeName == '#text') {
20598             
20599             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20600         }
20601         
20602         
20603         var ret = '';
20604         if (nodeName != 'BODY') {
20605              
20606             var i = 0;
20607             // Prints the node tagName, such as <A>, <IMG>, etc
20608             if (tagName) {
20609                 var attr = [];
20610                 for(i = 0; i < currentElement.attributes.length;i++) {
20611                     // quoting?
20612                     var aname = currentElement.attributes.item(i).name;
20613                     if (!currentElement.attributes.item(i).value.length) {
20614                         continue;
20615                     }
20616                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20617                 }
20618                 
20619                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20620             } 
20621             else {
20622                 
20623                 // eack
20624             }
20625         } else {
20626             tagName = false;
20627         }
20628         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20629             return ret;
20630         }
20631         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20632             nopadtext = true;
20633         }
20634         
20635         
20636         // Traverse the tree
20637         i = 0;
20638         var currentElementChild = currentElement.childNodes.item(i);
20639         var allText = true;
20640         var innerHTML  = '';
20641         lastnode = '';
20642         while (currentElementChild) {
20643             // Formatting code (indent the tree so it looks nice on the screen)
20644             var nopad = nopadtext;
20645             if (lastnode == 'SPAN') {
20646                 nopad  = true;
20647             }
20648             // text
20649             if  (currentElementChild.nodeName == '#text') {
20650                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20651                 toadd = nopadtext ? toadd : toadd.trim();
20652                 if (!nopad && toadd.length > 80) {
20653                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20654                 }
20655                 innerHTML  += toadd;
20656                 
20657                 i++;
20658                 currentElementChild = currentElement.childNodes.item(i);
20659                 lastNode = '';
20660                 continue;
20661             }
20662             allText = false;
20663             
20664             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20665                 
20666             // Recursively traverse the tree structure of the child node
20667             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20668             lastnode = currentElementChild.nodeName;
20669             i++;
20670             currentElementChild=currentElement.childNodes.item(i);
20671         }
20672         
20673         ret += innerHTML;
20674         
20675         if (!allText) {
20676                 // The remaining code is mostly for formatting the tree
20677             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20678         }
20679         
20680         
20681         if (tagName) {
20682             ret+= "</"+tagName+">";
20683         }
20684         return ret;
20685         
20686     },
20687         
20688     applyBlacklists : function()
20689     {
20690         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20691         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20692         
20693         this.white = [];
20694         this.black = [];
20695         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20696             if (b.indexOf(tag) > -1) {
20697                 return;
20698             }
20699             this.white.push(tag);
20700             
20701         }, this);
20702         
20703         Roo.each(w, function(tag) {
20704             if (b.indexOf(tag) > -1) {
20705                 return;
20706             }
20707             if (this.white.indexOf(tag) > -1) {
20708                 return;
20709             }
20710             this.white.push(tag);
20711             
20712         }, this);
20713         
20714         
20715         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20716             if (w.indexOf(tag) > -1) {
20717                 return;
20718             }
20719             this.black.push(tag);
20720             
20721         }, this);
20722         
20723         Roo.each(b, function(tag) {
20724             if (w.indexOf(tag) > -1) {
20725                 return;
20726             }
20727             if (this.black.indexOf(tag) > -1) {
20728                 return;
20729             }
20730             this.black.push(tag);
20731             
20732         }, this);
20733         
20734         
20735         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20736         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20737         
20738         this.cwhite = [];
20739         this.cblack = [];
20740         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20741             if (b.indexOf(tag) > -1) {
20742                 return;
20743             }
20744             this.cwhite.push(tag);
20745             
20746         }, this);
20747         
20748         Roo.each(w, function(tag) {
20749             if (b.indexOf(tag) > -1) {
20750                 return;
20751             }
20752             if (this.cwhite.indexOf(tag) > -1) {
20753                 return;
20754             }
20755             this.cwhite.push(tag);
20756             
20757         }, this);
20758         
20759         
20760         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20761             if (w.indexOf(tag) > -1) {
20762                 return;
20763             }
20764             this.cblack.push(tag);
20765             
20766         }, this);
20767         
20768         Roo.each(b, function(tag) {
20769             if (w.indexOf(tag) > -1) {
20770                 return;
20771             }
20772             if (this.cblack.indexOf(tag) > -1) {
20773                 return;
20774             }
20775             this.cblack.push(tag);
20776             
20777         }, this);
20778     },
20779     
20780     setStylesheets : function(stylesheets)
20781     {
20782         if(typeof(stylesheets) == 'string'){
20783             Roo.get(this.iframe.contentDocument.head).createChild({
20784                 tag : 'link',
20785                 rel : 'stylesheet',
20786                 type : 'text/css',
20787                 href : stylesheets
20788             });
20789             
20790             return;
20791         }
20792         var _this = this;
20793      
20794         Roo.each(stylesheets, function(s) {
20795             if(!s.length){
20796                 return;
20797             }
20798             
20799             Roo.get(_this.iframe.contentDocument.head).createChild({
20800                 tag : 'link',
20801                 rel : 'stylesheet',
20802                 type : 'text/css',
20803                 href : s
20804             });
20805         });
20806
20807         
20808     },
20809     
20810     removeStylesheets : function()
20811     {
20812         var _this = this;
20813         
20814         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20815             s.remove();
20816         });
20817     }
20818     
20819     // hide stuff that is not compatible
20820     /**
20821      * @event blur
20822      * @hide
20823      */
20824     /**
20825      * @event change
20826      * @hide
20827      */
20828     /**
20829      * @event focus
20830      * @hide
20831      */
20832     /**
20833      * @event specialkey
20834      * @hide
20835      */
20836     /**
20837      * @cfg {String} fieldClass @hide
20838      */
20839     /**
20840      * @cfg {String} focusClass @hide
20841      */
20842     /**
20843      * @cfg {String} autoCreate @hide
20844      */
20845     /**
20846      * @cfg {String} inputType @hide
20847      */
20848     /**
20849      * @cfg {String} invalidClass @hide
20850      */
20851     /**
20852      * @cfg {String} invalidText @hide
20853      */
20854     /**
20855      * @cfg {String} msgFx @hide
20856      */
20857     /**
20858      * @cfg {String} validateOnBlur @hide
20859      */
20860 });
20861
20862 Roo.HtmlEditorCore.white = [
20863         'area', 'br', 'img', 'input', 'hr', 'wbr',
20864         
20865        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20866        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20867        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20868        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20869        'table',   'ul',         'xmp', 
20870        
20871        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20872       'thead',   'tr', 
20873      
20874       'dir', 'menu', 'ol', 'ul', 'dl',
20875        
20876       'embed',  'object'
20877 ];
20878
20879
20880 Roo.HtmlEditorCore.black = [
20881     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20882         'applet', // 
20883         'base',   'basefont', 'bgsound', 'blink',  'body', 
20884         'frame',  'frameset', 'head',    'html',   'ilayer', 
20885         'iframe', 'layer',  'link',     'meta',    'object',   
20886         'script', 'style' ,'title',  'xml' // clean later..
20887 ];
20888 Roo.HtmlEditorCore.clean = [
20889     'script', 'style', 'title', 'xml'
20890 ];
20891 Roo.HtmlEditorCore.remove = [
20892     'font'
20893 ];
20894 // attributes..
20895
20896 Roo.HtmlEditorCore.ablack = [
20897     'on'
20898 ];
20899     
20900 Roo.HtmlEditorCore.aclean = [ 
20901     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20902 ];
20903
20904 // protocols..
20905 Roo.HtmlEditorCore.pwhite= [
20906         'http',  'https',  'mailto'
20907 ];
20908
20909 // white listed style attributes.
20910 Roo.HtmlEditorCore.cwhite= [
20911       //  'text-align', /// default is to allow most things..
20912       
20913          
20914 //        'font-size'//??
20915 ];
20916
20917 // black listed style attributes.
20918 Roo.HtmlEditorCore.cblack= [
20919       //  'font-size' -- this can be set by the project 
20920 ];
20921
20922
20923 Roo.HtmlEditorCore.swapCodes   =[ 
20924     [    8211, "--" ], 
20925     [    8212, "--" ], 
20926     [    8216,  "'" ],  
20927     [    8217, "'" ],  
20928     [    8220, '"' ],  
20929     [    8221, '"' ],  
20930     [    8226, "*" ],  
20931     [    8230, "..." ]
20932 ]; 
20933
20934     /*
20935  * - LGPL
20936  *
20937  * HtmlEditor
20938  * 
20939  */
20940
20941 /**
20942  * @class Roo.bootstrap.HtmlEditor
20943  * @extends Roo.bootstrap.TextArea
20944  * Bootstrap HtmlEditor class
20945
20946  * @constructor
20947  * Create a new HtmlEditor
20948  * @param {Object} config The config object
20949  */
20950
20951 Roo.bootstrap.HtmlEditor = function(config){
20952     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
20953     if (!this.toolbars) {
20954         this.toolbars = [];
20955     }
20956     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
20957     this.addEvents({
20958             /**
20959              * @event initialize
20960              * Fires when the editor is fully initialized (including the iframe)
20961              * @param {HtmlEditor} this
20962              */
20963             initialize: true,
20964             /**
20965              * @event activate
20966              * Fires when the editor is first receives the focus. Any insertion must wait
20967              * until after this event.
20968              * @param {HtmlEditor} this
20969              */
20970             activate: true,
20971              /**
20972              * @event beforesync
20973              * Fires before the textarea is updated with content from the editor iframe. Return false
20974              * to cancel the sync.
20975              * @param {HtmlEditor} this
20976              * @param {String} html
20977              */
20978             beforesync: true,
20979              /**
20980              * @event beforepush
20981              * Fires before the iframe editor is updated with content from the textarea. Return false
20982              * to cancel the push.
20983              * @param {HtmlEditor} this
20984              * @param {String} html
20985              */
20986             beforepush: true,
20987              /**
20988              * @event sync
20989              * Fires when the textarea is updated with content from the editor iframe.
20990              * @param {HtmlEditor} this
20991              * @param {String} html
20992              */
20993             sync: true,
20994              /**
20995              * @event push
20996              * Fires when the iframe editor is updated with content from the textarea.
20997              * @param {HtmlEditor} this
20998              * @param {String} html
20999              */
21000             push: true,
21001              /**
21002              * @event editmodechange
21003              * Fires when the editor switches edit modes
21004              * @param {HtmlEditor} this
21005              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21006              */
21007             editmodechange: true,
21008             /**
21009              * @event editorevent
21010              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21011              * @param {HtmlEditor} this
21012              */
21013             editorevent: true,
21014             /**
21015              * @event firstfocus
21016              * Fires when on first focus - needed by toolbars..
21017              * @param {HtmlEditor} this
21018              */
21019             firstfocus: true,
21020             /**
21021              * @event autosave
21022              * Auto save the htmlEditor value as a file into Events
21023              * @param {HtmlEditor} this
21024              */
21025             autosave: true,
21026             /**
21027              * @event savedpreview
21028              * preview the saved version of htmlEditor
21029              * @param {HtmlEditor} this
21030              */
21031             savedpreview: true
21032         });
21033 };
21034
21035
21036 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21037     
21038     
21039       /**
21040      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21041      */
21042     toolbars : false,
21043    
21044      /**
21045      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21046      *                        Roo.resizable.
21047      */
21048     resizable : false,
21049      /**
21050      * @cfg {Number} height (in pixels)
21051      */   
21052     height: 300,
21053    /**
21054      * @cfg {Number} width (in pixels)
21055      */   
21056     width: false,
21057     
21058     /**
21059      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21060      * 
21061      */
21062     stylesheets: false,
21063     
21064     // id of frame..
21065     frameId: false,
21066     
21067     // private properties
21068     validationEvent : false,
21069     deferHeight: true,
21070     initialized : false,
21071     activated : false,
21072     
21073     onFocus : Roo.emptyFn,
21074     iframePad:3,
21075     hideMode:'offsets',
21076     
21077     
21078     tbContainer : false,
21079     
21080     toolbarContainer :function() {
21081         return this.wrap.select('.x-html-editor-tb',true).first();
21082     },
21083
21084     /**
21085      * Protected method that will not generally be called directly. It
21086      * is called when the editor creates its toolbar. Override this method if you need to
21087      * add custom toolbar buttons.
21088      * @param {HtmlEditor} editor
21089      */
21090     createToolbar : function(){
21091         
21092         Roo.log("create toolbars");
21093         
21094         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21095         this.toolbars[0].render(this.toolbarContainer());
21096         
21097         return;
21098         
21099 //        if (!editor.toolbars || !editor.toolbars.length) {
21100 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21101 //        }
21102 //        
21103 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21104 //            editor.toolbars[i] = Roo.factory(
21105 //                    typeof(editor.toolbars[i]) == 'string' ?
21106 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21107 //                Roo.bootstrap.HtmlEditor);
21108 //            editor.toolbars[i].init(editor);
21109 //        }
21110     },
21111
21112      
21113     // private
21114     onRender : function(ct, position)
21115     {
21116        // Roo.log("Call onRender: " + this.xtype);
21117         var _t = this;
21118         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21119       
21120         this.wrap = this.inputEl().wrap({
21121             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21122         });
21123         
21124         this.editorcore.onRender(ct, position);
21125          
21126         if (this.resizable) {
21127             this.resizeEl = new Roo.Resizable(this.wrap, {
21128                 pinned : true,
21129                 wrap: true,
21130                 dynamic : true,
21131                 minHeight : this.height,
21132                 height: this.height,
21133                 handles : this.resizable,
21134                 width: this.width,
21135                 listeners : {
21136                     resize : function(r, w, h) {
21137                         _t.onResize(w,h); // -something
21138                     }
21139                 }
21140             });
21141             
21142         }
21143         this.createToolbar(this);
21144        
21145         
21146         if(!this.width && this.resizable){
21147             this.setSize(this.wrap.getSize());
21148         }
21149         if (this.resizeEl) {
21150             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21151             // should trigger onReize..
21152         }
21153         
21154     },
21155
21156     // private
21157     onResize : function(w, h)
21158     {
21159         Roo.log('resize: ' +w + ',' + h );
21160         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21161         var ew = false;
21162         var eh = false;
21163         
21164         if(this.inputEl() ){
21165             if(typeof w == 'number'){
21166                 var aw = w - this.wrap.getFrameWidth('lr');
21167                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21168                 ew = aw;
21169             }
21170             if(typeof h == 'number'){
21171                  var tbh = -11;  // fixme it needs to tool bar size!
21172                 for (var i =0; i < this.toolbars.length;i++) {
21173                     // fixme - ask toolbars for heights?
21174                     tbh += this.toolbars[i].el.getHeight();
21175                     //if (this.toolbars[i].footer) {
21176                     //    tbh += this.toolbars[i].footer.el.getHeight();
21177                     //}
21178                 }
21179               
21180                 
21181                 
21182                 
21183                 
21184                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21185                 ah -= 5; // knock a few pixes off for look..
21186                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21187                 var eh = ah;
21188             }
21189         }
21190         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21191         this.editorcore.onResize(ew,eh);
21192         
21193     },
21194
21195     /**
21196      * Toggles the editor between standard and source edit mode.
21197      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21198      */
21199     toggleSourceEdit : function(sourceEditMode)
21200     {
21201         this.editorcore.toggleSourceEdit(sourceEditMode);
21202         
21203         if(this.editorcore.sourceEditMode){
21204             Roo.log('editor - showing textarea');
21205             
21206 //            Roo.log('in');
21207 //            Roo.log(this.syncValue());
21208             this.syncValue();
21209             this.inputEl().removeClass(['hide', 'x-hidden']);
21210             this.inputEl().dom.removeAttribute('tabIndex');
21211             this.inputEl().focus();
21212         }else{
21213             Roo.log('editor - hiding textarea');
21214 //            Roo.log('out')
21215 //            Roo.log(this.pushValue()); 
21216             this.pushValue();
21217             
21218             this.inputEl().addClass(['hide', 'x-hidden']);
21219             this.inputEl().dom.setAttribute('tabIndex', -1);
21220             //this.deferFocus();
21221         }
21222          
21223         if(this.resizable){
21224             this.setSize(this.wrap.getSize());
21225         }
21226         
21227         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21228     },
21229  
21230     // private (for BoxComponent)
21231     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21232
21233     // private (for BoxComponent)
21234     getResizeEl : function(){
21235         return this.wrap;
21236     },
21237
21238     // private (for BoxComponent)
21239     getPositionEl : function(){
21240         return this.wrap;
21241     },
21242
21243     // private
21244     initEvents : function(){
21245         this.originalValue = this.getValue();
21246     },
21247
21248 //    /**
21249 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21250 //     * @method
21251 //     */
21252 //    markInvalid : Roo.emptyFn,
21253 //    /**
21254 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21255 //     * @method
21256 //     */
21257 //    clearInvalid : Roo.emptyFn,
21258
21259     setValue : function(v){
21260         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21261         this.editorcore.pushValue();
21262     },
21263
21264      
21265     // private
21266     deferFocus : function(){
21267         this.focus.defer(10, this);
21268     },
21269
21270     // doc'ed in Field
21271     focus : function(){
21272         this.editorcore.focus();
21273         
21274     },
21275       
21276
21277     // private
21278     onDestroy : function(){
21279         
21280         
21281         
21282         if(this.rendered){
21283             
21284             for (var i =0; i < this.toolbars.length;i++) {
21285                 // fixme - ask toolbars for heights?
21286                 this.toolbars[i].onDestroy();
21287             }
21288             
21289             this.wrap.dom.innerHTML = '';
21290             this.wrap.remove();
21291         }
21292     },
21293
21294     // private
21295     onFirstFocus : function(){
21296         //Roo.log("onFirstFocus");
21297         this.editorcore.onFirstFocus();
21298          for (var i =0; i < this.toolbars.length;i++) {
21299             this.toolbars[i].onFirstFocus();
21300         }
21301         
21302     },
21303     
21304     // private
21305     syncValue : function()
21306     {   
21307         this.editorcore.syncValue();
21308     },
21309     
21310     pushValue : function()
21311     {   
21312         this.editorcore.pushValue();
21313     }
21314      
21315     
21316     // hide stuff that is not compatible
21317     /**
21318      * @event blur
21319      * @hide
21320      */
21321     /**
21322      * @event change
21323      * @hide
21324      */
21325     /**
21326      * @event focus
21327      * @hide
21328      */
21329     /**
21330      * @event specialkey
21331      * @hide
21332      */
21333     /**
21334      * @cfg {String} fieldClass @hide
21335      */
21336     /**
21337      * @cfg {String} focusClass @hide
21338      */
21339     /**
21340      * @cfg {String} autoCreate @hide
21341      */
21342     /**
21343      * @cfg {String} inputType @hide
21344      */
21345     /**
21346      * @cfg {String} invalidClass @hide
21347      */
21348     /**
21349      * @cfg {String} invalidText @hide
21350      */
21351     /**
21352      * @cfg {String} msgFx @hide
21353      */
21354     /**
21355      * @cfg {String} validateOnBlur @hide
21356      */
21357 });
21358  
21359     
21360    
21361    
21362    
21363       
21364 Roo.namespace('Roo.bootstrap.htmleditor');
21365 /**
21366  * @class Roo.bootstrap.HtmlEditorToolbar1
21367  * Basic Toolbar
21368  * 
21369  * Usage:
21370  *
21371  new Roo.bootstrap.HtmlEditor({
21372     ....
21373     toolbars : [
21374         new Roo.bootstrap.HtmlEditorToolbar1({
21375             disable : { fonts: 1 , format: 1, ..., ... , ...],
21376             btns : [ .... ]
21377         })
21378     }
21379      
21380  * 
21381  * @cfg {Object} disable List of elements to disable..
21382  * @cfg {Array} btns List of additional buttons.
21383  * 
21384  * 
21385  * NEEDS Extra CSS? 
21386  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21387  */
21388  
21389 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21390 {
21391     
21392     Roo.apply(this, config);
21393     
21394     // default disabled, based on 'good practice'..
21395     this.disable = this.disable || {};
21396     Roo.applyIf(this.disable, {
21397         fontSize : true,
21398         colors : true,
21399         specialElements : true
21400     });
21401     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21402     
21403     this.editor = config.editor;
21404     this.editorcore = config.editor.editorcore;
21405     
21406     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21407     
21408     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21409     // dont call parent... till later.
21410 }
21411 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21412      
21413     bar : true,
21414     
21415     editor : false,
21416     editorcore : false,
21417     
21418     
21419     formats : [
21420         "p" ,  
21421         "h1","h2","h3","h4","h5","h6", 
21422         "pre", "code", 
21423         "abbr", "acronym", "address", "cite", "samp", "var",
21424         'div','span'
21425     ],
21426     
21427     onRender : function(ct, position)
21428     {
21429        // Roo.log("Call onRender: " + this.xtype);
21430         
21431        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21432        Roo.log(this.el);
21433        this.el.dom.style.marginBottom = '0';
21434        var _this = this;
21435        var editorcore = this.editorcore;
21436        var editor= this.editor;
21437        
21438        var children = [];
21439        var btn = function(id,cmd , toggle, handler){
21440        
21441             var  event = toggle ? 'toggle' : 'click';
21442        
21443             var a = {
21444                 size : 'sm',
21445                 xtype: 'Button',
21446                 xns: Roo.bootstrap,
21447                 glyphicon : id,
21448                 cmd : id || cmd,
21449                 enableToggle:toggle !== false,
21450                 //html : 'submit'
21451                 pressed : toggle ? false : null,
21452                 listeners : {}
21453             };
21454             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21455                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21456             };
21457             children.push(a);
21458             return a;
21459        }
21460         
21461         var style = {
21462                 xtype: 'Button',
21463                 size : 'sm',
21464                 xns: Roo.bootstrap,
21465                 glyphicon : 'font',
21466                 //html : 'submit'
21467                 menu : {
21468                     xtype: 'Menu',
21469                     xns: Roo.bootstrap,
21470                     items:  []
21471                 }
21472         };
21473         Roo.each(this.formats, function(f) {
21474             style.menu.items.push({
21475                 xtype :'MenuItem',
21476                 xns: Roo.bootstrap,
21477                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21478                 tagname : f,
21479                 listeners : {
21480                     click : function()
21481                     {
21482                         editorcore.insertTag(this.tagname);
21483                         editor.focus();
21484                     }
21485                 }
21486                 
21487             });
21488         });
21489          children.push(style);   
21490             
21491             
21492         btn('bold',false,true);
21493         btn('italic',false,true);
21494         btn('align-left', 'justifyleft',true);
21495         btn('align-center', 'justifycenter',true);
21496         btn('align-right' , 'justifyright',true);
21497         btn('link', false, false, function(btn) {
21498             //Roo.log("create link?");
21499             var url = prompt(this.createLinkText, this.defaultLinkValue);
21500             if(url && url != 'http:/'+'/'){
21501                 this.editorcore.relayCmd('createlink', url);
21502             }
21503         }),
21504         btn('list','insertunorderedlist',true);
21505         btn('pencil', false,true, function(btn){
21506                 Roo.log(this);
21507                 
21508                 this.toggleSourceEdit(btn.pressed);
21509         });
21510         /*
21511         var cog = {
21512                 xtype: 'Button',
21513                 size : 'sm',
21514                 xns: Roo.bootstrap,
21515                 glyphicon : 'cog',
21516                 //html : 'submit'
21517                 menu : {
21518                     xtype: 'Menu',
21519                     xns: Roo.bootstrap,
21520                     items:  []
21521                 }
21522         };
21523         
21524         cog.menu.items.push({
21525             xtype :'MenuItem',
21526             xns: Roo.bootstrap,
21527             html : Clean styles,
21528             tagname : f,
21529             listeners : {
21530                 click : function()
21531                 {
21532                     editorcore.insertTag(this.tagname);
21533                     editor.focus();
21534                 }
21535             }
21536             
21537         });
21538        */
21539         
21540          
21541        this.xtype = 'NavSimplebar';
21542         
21543         for(var i=0;i< children.length;i++) {
21544             
21545             this.buttons.add(this.addxtypeChild(children[i]));
21546             
21547         }
21548         
21549         editor.on('editorevent', this.updateToolbar, this);
21550     },
21551     onBtnClick : function(id)
21552     {
21553        this.editorcore.relayCmd(id);
21554        this.editorcore.focus();
21555     },
21556     
21557     /**
21558      * Protected method that will not generally be called directly. It triggers
21559      * a toolbar update by reading the markup state of the current selection in the editor.
21560      */
21561     updateToolbar: function(){
21562
21563         if(!this.editorcore.activated){
21564             this.editor.onFirstFocus(); // is this neeed?
21565             return;
21566         }
21567
21568         var btns = this.buttons; 
21569         var doc = this.editorcore.doc;
21570         btns.get('bold').setActive(doc.queryCommandState('bold'));
21571         btns.get('italic').setActive(doc.queryCommandState('italic'));
21572         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21573         
21574         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21575         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21576         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21577         
21578         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21579         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21580          /*
21581         
21582         var ans = this.editorcore.getAllAncestors();
21583         if (this.formatCombo) {
21584             
21585             
21586             var store = this.formatCombo.store;
21587             this.formatCombo.setValue("");
21588             for (var i =0; i < ans.length;i++) {
21589                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21590                     // select it..
21591                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21592                     break;
21593                 }
21594             }
21595         }
21596         
21597         
21598         
21599         // hides menus... - so this cant be on a menu...
21600         Roo.bootstrap.MenuMgr.hideAll();
21601         */
21602         Roo.bootstrap.MenuMgr.hideAll();
21603         //this.editorsyncValue();
21604     },
21605     onFirstFocus: function() {
21606         this.buttons.each(function(item){
21607            item.enable();
21608         });
21609     },
21610     toggleSourceEdit : function(sourceEditMode){
21611         
21612           
21613         if(sourceEditMode){
21614             Roo.log("disabling buttons");
21615            this.buttons.each( function(item){
21616                 if(item.cmd != 'pencil'){
21617                     item.disable();
21618                 }
21619             });
21620           
21621         }else{
21622             Roo.log("enabling buttons");
21623             if(this.editorcore.initialized){
21624                 this.buttons.each( function(item){
21625                     item.enable();
21626                 });
21627             }
21628             
21629         }
21630         Roo.log("calling toggole on editor");
21631         // tell the editor that it's been pressed..
21632         this.editor.toggleSourceEdit(sourceEditMode);
21633        
21634     }
21635 });
21636
21637
21638
21639
21640
21641 /**
21642  * @class Roo.bootstrap.Table.AbstractSelectionModel
21643  * @extends Roo.util.Observable
21644  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21645  * implemented by descendant classes.  This class should not be directly instantiated.
21646  * @constructor
21647  */
21648 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21649     this.locked = false;
21650     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21651 };
21652
21653
21654 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21655     /** @ignore Called by the grid automatically. Do not call directly. */
21656     init : function(grid){
21657         this.grid = grid;
21658         this.initEvents();
21659     },
21660
21661     /**
21662      * Locks the selections.
21663      */
21664     lock : function(){
21665         this.locked = true;
21666     },
21667
21668     /**
21669      * Unlocks the selections.
21670      */
21671     unlock : function(){
21672         this.locked = false;
21673     },
21674
21675     /**
21676      * Returns true if the selections are locked.
21677      * @return {Boolean}
21678      */
21679     isLocked : function(){
21680         return this.locked;
21681     }
21682 });
21683 /**
21684  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21685  * @class Roo.bootstrap.Table.RowSelectionModel
21686  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21687  * It supports multiple selections and keyboard selection/navigation. 
21688  * @constructor
21689  * @param {Object} config
21690  */
21691
21692 Roo.bootstrap.Table.RowSelectionModel = function(config){
21693     Roo.apply(this, config);
21694     this.selections = new Roo.util.MixedCollection(false, function(o){
21695         return o.id;
21696     });
21697
21698     this.last = false;
21699     this.lastActive = false;
21700
21701     this.addEvents({
21702         /**
21703              * @event selectionchange
21704              * Fires when the selection changes
21705              * @param {SelectionModel} this
21706              */
21707             "selectionchange" : true,
21708         /**
21709              * @event afterselectionchange
21710              * Fires after the selection changes (eg. by key press or clicking)
21711              * @param {SelectionModel} this
21712              */
21713             "afterselectionchange" : true,
21714         /**
21715              * @event beforerowselect
21716              * Fires when a row is selected being selected, return false to cancel.
21717              * @param {SelectionModel} this
21718              * @param {Number} rowIndex The selected index
21719              * @param {Boolean} keepExisting False if other selections will be cleared
21720              */
21721             "beforerowselect" : true,
21722         /**
21723              * @event rowselect
21724              * Fires when a row is selected.
21725              * @param {SelectionModel} this
21726              * @param {Number} rowIndex The selected index
21727              * @param {Roo.data.Record} r The record
21728              */
21729             "rowselect" : true,
21730         /**
21731              * @event rowdeselect
21732              * Fires when a row is deselected.
21733              * @param {SelectionModel} this
21734              * @param {Number} rowIndex The selected index
21735              */
21736         "rowdeselect" : true
21737     });
21738     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21739     this.locked = false;
21740 };
21741
21742 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21743     /**
21744      * @cfg {Boolean} singleSelect
21745      * True to allow selection of only one row at a time (defaults to false)
21746      */
21747     singleSelect : false,
21748
21749     // private
21750     initEvents : function(){
21751
21752         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21753             this.grid.on("mousedown", this.handleMouseDown, this);
21754         }else{ // allow click to work like normal
21755             this.grid.on("rowclick", this.handleDragableRowClick, this);
21756         }
21757
21758         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21759             "up" : function(e){
21760                 if(!e.shiftKey){
21761                     this.selectPrevious(e.shiftKey);
21762                 }else if(this.last !== false && this.lastActive !== false){
21763                     var last = this.last;
21764                     this.selectRange(this.last,  this.lastActive-1);
21765                     this.grid.getView().focusRow(this.lastActive);
21766                     if(last !== false){
21767                         this.last = last;
21768                     }
21769                 }else{
21770                     this.selectFirstRow();
21771                 }
21772                 this.fireEvent("afterselectionchange", this);
21773             },
21774             "down" : function(e){
21775                 if(!e.shiftKey){
21776                     this.selectNext(e.shiftKey);
21777                 }else if(this.last !== false && this.lastActive !== false){
21778                     var last = this.last;
21779                     this.selectRange(this.last,  this.lastActive+1);
21780                     this.grid.getView().focusRow(this.lastActive);
21781                     if(last !== false){
21782                         this.last = last;
21783                     }
21784                 }else{
21785                     this.selectFirstRow();
21786                 }
21787                 this.fireEvent("afterselectionchange", this);
21788             },
21789             scope: this
21790         });
21791
21792         var view = this.grid.view;
21793         view.on("refresh", this.onRefresh, this);
21794         view.on("rowupdated", this.onRowUpdated, this);
21795         view.on("rowremoved", this.onRemove, this);
21796     },
21797
21798     // private
21799     onRefresh : function(){
21800         var ds = this.grid.dataSource, i, v = this.grid.view;
21801         var s = this.selections;
21802         s.each(function(r){
21803             if((i = ds.indexOfId(r.id)) != -1){
21804                 v.onRowSelect(i);
21805             }else{
21806                 s.remove(r);
21807             }
21808         });
21809     },
21810
21811     // private
21812     onRemove : function(v, index, r){
21813         this.selections.remove(r);
21814     },
21815
21816     // private
21817     onRowUpdated : function(v, index, r){
21818         if(this.isSelected(r)){
21819             v.onRowSelect(index);
21820         }
21821     },
21822
21823     /**
21824      * Select records.
21825      * @param {Array} records The records to select
21826      * @param {Boolean} keepExisting (optional) True to keep existing selections
21827      */
21828     selectRecords : function(records, keepExisting){
21829         if(!keepExisting){
21830             this.clearSelections();
21831         }
21832         var ds = this.grid.dataSource;
21833         for(var i = 0, len = records.length; i < len; i++){
21834             this.selectRow(ds.indexOf(records[i]), true);
21835         }
21836     },
21837
21838     /**
21839      * Gets the number of selected rows.
21840      * @return {Number}
21841      */
21842     getCount : function(){
21843         return this.selections.length;
21844     },
21845
21846     /**
21847      * Selects the first row in the grid.
21848      */
21849     selectFirstRow : function(){
21850         this.selectRow(0);
21851     },
21852
21853     /**
21854      * Select the last row.
21855      * @param {Boolean} keepExisting (optional) True to keep existing selections
21856      */
21857     selectLastRow : function(keepExisting){
21858         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21859     },
21860
21861     /**
21862      * Selects the row immediately following the last selected row.
21863      * @param {Boolean} keepExisting (optional) True to keep existing selections
21864      */
21865     selectNext : function(keepExisting){
21866         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21867             this.selectRow(this.last+1, keepExisting);
21868             this.grid.getView().focusRow(this.last);
21869         }
21870     },
21871
21872     /**
21873      * Selects the row that precedes the last selected row.
21874      * @param {Boolean} keepExisting (optional) True to keep existing selections
21875      */
21876     selectPrevious : function(keepExisting){
21877         if(this.last){
21878             this.selectRow(this.last-1, keepExisting);
21879             this.grid.getView().focusRow(this.last);
21880         }
21881     },
21882
21883     /**
21884      * Returns the selected records
21885      * @return {Array} Array of selected records
21886      */
21887     getSelections : function(){
21888         return [].concat(this.selections.items);
21889     },
21890
21891     /**
21892      * Returns the first selected record.
21893      * @return {Record}
21894      */
21895     getSelected : function(){
21896         return this.selections.itemAt(0);
21897     },
21898
21899
21900     /**
21901      * Clears all selections.
21902      */
21903     clearSelections : function(fast){
21904         if(this.locked) {
21905             return;
21906         }
21907         if(fast !== true){
21908             var ds = this.grid.dataSource;
21909             var s = this.selections;
21910             s.each(function(r){
21911                 this.deselectRow(ds.indexOfId(r.id));
21912             }, this);
21913             s.clear();
21914         }else{
21915             this.selections.clear();
21916         }
21917         this.last = false;
21918     },
21919
21920
21921     /**
21922      * Selects all rows.
21923      */
21924     selectAll : function(){
21925         if(this.locked) {
21926             return;
21927         }
21928         this.selections.clear();
21929         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
21930             this.selectRow(i, true);
21931         }
21932     },
21933
21934     /**
21935      * Returns True if there is a selection.
21936      * @return {Boolean}
21937      */
21938     hasSelection : function(){
21939         return this.selections.length > 0;
21940     },
21941
21942     /**
21943      * Returns True if the specified row is selected.
21944      * @param {Number/Record} record The record or index of the record to check
21945      * @return {Boolean}
21946      */
21947     isSelected : function(index){
21948         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
21949         return (r && this.selections.key(r.id) ? true : false);
21950     },
21951
21952     /**
21953      * Returns True if the specified record id is selected.
21954      * @param {String} id The id of record to check
21955      * @return {Boolean}
21956      */
21957     isIdSelected : function(id){
21958         return (this.selections.key(id) ? true : false);
21959     },
21960
21961     // private
21962     handleMouseDown : function(e, t){
21963         var view = this.grid.getView(), rowIndex;
21964         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
21965             return;
21966         };
21967         if(e.shiftKey && this.last !== false){
21968             var last = this.last;
21969             this.selectRange(last, rowIndex, e.ctrlKey);
21970             this.last = last; // reset the last
21971             view.focusRow(rowIndex);
21972         }else{
21973             var isSelected = this.isSelected(rowIndex);
21974             if(e.button !== 0 && isSelected){
21975                 view.focusRow(rowIndex);
21976             }else if(e.ctrlKey && isSelected){
21977                 this.deselectRow(rowIndex);
21978             }else if(!isSelected){
21979                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
21980                 view.focusRow(rowIndex);
21981             }
21982         }
21983         this.fireEvent("afterselectionchange", this);
21984     },
21985     // private
21986     handleDragableRowClick :  function(grid, rowIndex, e) 
21987     {
21988         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
21989             this.selectRow(rowIndex, false);
21990             grid.view.focusRow(rowIndex);
21991              this.fireEvent("afterselectionchange", this);
21992         }
21993     },
21994     
21995     /**
21996      * Selects multiple rows.
21997      * @param {Array} rows Array of the indexes of the row to select
21998      * @param {Boolean} keepExisting (optional) True to keep existing selections
21999      */
22000     selectRows : function(rows, keepExisting){
22001         if(!keepExisting){
22002             this.clearSelections();
22003         }
22004         for(var i = 0, len = rows.length; i < len; i++){
22005             this.selectRow(rows[i], true);
22006         }
22007     },
22008
22009     /**
22010      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22011      * @param {Number} startRow The index of the first row in the range
22012      * @param {Number} endRow The index of the last row in the range
22013      * @param {Boolean} keepExisting (optional) True to retain existing selections
22014      */
22015     selectRange : function(startRow, endRow, keepExisting){
22016         if(this.locked) {
22017             return;
22018         }
22019         if(!keepExisting){
22020             this.clearSelections();
22021         }
22022         if(startRow <= endRow){
22023             for(var i = startRow; i <= endRow; i++){
22024                 this.selectRow(i, true);
22025             }
22026         }else{
22027             for(var i = startRow; i >= endRow; i--){
22028                 this.selectRow(i, true);
22029             }
22030         }
22031     },
22032
22033     /**
22034      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22035      * @param {Number} startRow The index of the first row in the range
22036      * @param {Number} endRow The index of the last row in the range
22037      */
22038     deselectRange : function(startRow, endRow, preventViewNotify){
22039         if(this.locked) {
22040             return;
22041         }
22042         for(var i = startRow; i <= endRow; i++){
22043             this.deselectRow(i, preventViewNotify);
22044         }
22045     },
22046
22047     /**
22048      * Selects a row.
22049      * @param {Number} row The index of the row to select
22050      * @param {Boolean} keepExisting (optional) True to keep existing selections
22051      */
22052     selectRow : function(index, keepExisting, preventViewNotify){
22053         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22054             return;
22055         }
22056         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22057             if(!keepExisting || this.singleSelect){
22058                 this.clearSelections();
22059             }
22060             var r = this.grid.dataSource.getAt(index);
22061             this.selections.add(r);
22062             this.last = this.lastActive = index;
22063             if(!preventViewNotify){
22064                 this.grid.getView().onRowSelect(index);
22065             }
22066             this.fireEvent("rowselect", this, index, r);
22067             this.fireEvent("selectionchange", this);
22068         }
22069     },
22070
22071     /**
22072      * Deselects a row.
22073      * @param {Number} row The index of the row to deselect
22074      */
22075     deselectRow : function(index, preventViewNotify){
22076         if(this.locked) {
22077             return;
22078         }
22079         if(this.last == index){
22080             this.last = false;
22081         }
22082         if(this.lastActive == index){
22083             this.lastActive = false;
22084         }
22085         var r = this.grid.dataSource.getAt(index);
22086         this.selections.remove(r);
22087         if(!preventViewNotify){
22088             this.grid.getView().onRowDeselect(index);
22089         }
22090         this.fireEvent("rowdeselect", this, index);
22091         this.fireEvent("selectionchange", this);
22092     },
22093
22094     // private
22095     restoreLast : function(){
22096         if(this._last){
22097             this.last = this._last;
22098         }
22099     },
22100
22101     // private
22102     acceptsNav : function(row, col, cm){
22103         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22104     },
22105
22106     // private
22107     onEditorKey : function(field, e){
22108         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22109         if(k == e.TAB){
22110             e.stopEvent();
22111             ed.completeEdit();
22112             if(e.shiftKey){
22113                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22114             }else{
22115                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22116             }
22117         }else if(k == e.ENTER && !e.ctrlKey){
22118             e.stopEvent();
22119             ed.completeEdit();
22120             if(e.shiftKey){
22121                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22122             }else{
22123                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22124             }
22125         }else if(k == e.ESC){
22126             ed.cancelEdit();
22127         }
22128         if(newCell){
22129             g.startEditing(newCell[0], newCell[1]);
22130         }
22131     }
22132 });/*
22133  * Based on:
22134  * Ext JS Library 1.1.1
22135  * Copyright(c) 2006-2007, Ext JS, LLC.
22136  *
22137  * Originally Released Under LGPL - original licence link has changed is not relivant.
22138  *
22139  * Fork - LGPL
22140  * <script type="text/javascript">
22141  */
22142  
22143 /**
22144  * @class Roo.bootstrap.PagingToolbar
22145  * @extends Roo.bootstrap.NavSimplebar
22146  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22147  * @constructor
22148  * Create a new PagingToolbar
22149  * @param {Object} config The config object
22150  * @param {Roo.data.Store} store
22151  */
22152 Roo.bootstrap.PagingToolbar = function(config)
22153 {
22154     // old args format still supported... - xtype is prefered..
22155         // created from xtype...
22156     
22157     this.ds = config.dataSource;
22158     
22159     if (config.store && !this.ds) {
22160         this.store= Roo.factory(config.store, Roo.data);
22161         this.ds = this.store;
22162         this.ds.xmodule = this.xmodule || false;
22163     }
22164     
22165     this.toolbarItems = [];
22166     if (config.items) {
22167         this.toolbarItems = config.items;
22168     }
22169     
22170     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22171     
22172     this.cursor = 0;
22173     
22174     if (this.ds) { 
22175         this.bind(this.ds);
22176     }
22177     
22178     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22179     
22180 };
22181
22182 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22183     /**
22184      * @cfg {Roo.data.Store} dataSource
22185      * The underlying data store providing the paged data
22186      */
22187     /**
22188      * @cfg {String/HTMLElement/Element} container
22189      * container The id or element that will contain the toolbar
22190      */
22191     /**
22192      * @cfg {Boolean} displayInfo
22193      * True to display the displayMsg (defaults to false)
22194      */
22195     /**
22196      * @cfg {Number} pageSize
22197      * The number of records to display per page (defaults to 20)
22198      */
22199     pageSize: 20,
22200     /**
22201      * @cfg {String} displayMsg
22202      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22203      */
22204     displayMsg : 'Displaying {0} - {1} of {2}',
22205     /**
22206      * @cfg {String} emptyMsg
22207      * The message to display when no records are found (defaults to "No data to display")
22208      */
22209     emptyMsg : 'No data to display',
22210     /**
22211      * Customizable piece of the default paging text (defaults to "Page")
22212      * @type String
22213      */
22214     beforePageText : "Page",
22215     /**
22216      * Customizable piece of the default paging text (defaults to "of %0")
22217      * @type String
22218      */
22219     afterPageText : "of {0}",
22220     /**
22221      * Customizable piece of the default paging text (defaults to "First Page")
22222      * @type String
22223      */
22224     firstText : "First Page",
22225     /**
22226      * Customizable piece of the default paging text (defaults to "Previous Page")
22227      * @type String
22228      */
22229     prevText : "Previous Page",
22230     /**
22231      * Customizable piece of the default paging text (defaults to "Next Page")
22232      * @type String
22233      */
22234     nextText : "Next Page",
22235     /**
22236      * Customizable piece of the default paging text (defaults to "Last Page")
22237      * @type String
22238      */
22239     lastText : "Last Page",
22240     /**
22241      * Customizable piece of the default paging text (defaults to "Refresh")
22242      * @type String
22243      */
22244     refreshText : "Refresh",
22245
22246     buttons : false,
22247     // private
22248     onRender : function(ct, position) 
22249     {
22250         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22251         this.navgroup.parentId = this.id;
22252         this.navgroup.onRender(this.el, null);
22253         // add the buttons to the navgroup
22254         
22255         if(this.displayInfo){
22256             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22257             this.displayEl = this.el.select('.x-paging-info', true).first();
22258 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22259 //            this.displayEl = navel.el.select('span',true).first();
22260         }
22261         
22262         var _this = this;
22263         
22264         if(this.buttons){
22265             Roo.each(_this.buttons, function(e){ // this might need to use render????
22266                Roo.factory(e).onRender(_this.el, null);
22267             });
22268         }
22269             
22270         Roo.each(_this.toolbarItems, function(e) {
22271             _this.navgroup.addItem(e);
22272         });
22273         
22274         
22275         this.first = this.navgroup.addItem({
22276             tooltip: this.firstText,
22277             cls: "prev",
22278             icon : 'fa fa-backward',
22279             disabled: true,
22280             preventDefault: true,
22281             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22282         });
22283         
22284         this.prev =  this.navgroup.addItem({
22285             tooltip: this.prevText,
22286             cls: "prev",
22287             icon : 'fa fa-step-backward',
22288             disabled: true,
22289             preventDefault: true,
22290             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22291         });
22292     //this.addSeparator();
22293         
22294         
22295         var field = this.navgroup.addItem( {
22296             tagtype : 'span',
22297             cls : 'x-paging-position',
22298             
22299             html : this.beforePageText  +
22300                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22301                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22302          } ); //?? escaped?
22303         
22304         this.field = field.el.select('input', true).first();
22305         this.field.on("keydown", this.onPagingKeydown, this);
22306         this.field.on("focus", function(){this.dom.select();});
22307     
22308     
22309         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22310         //this.field.setHeight(18);
22311         //this.addSeparator();
22312         this.next = this.navgroup.addItem({
22313             tooltip: this.nextText,
22314             cls: "next",
22315             html : ' <i class="fa fa-step-forward">',
22316             disabled: true,
22317             preventDefault: true,
22318             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22319         });
22320         this.last = this.navgroup.addItem({
22321             tooltip: this.lastText,
22322             icon : 'fa fa-forward',
22323             cls: "next",
22324             disabled: true,
22325             preventDefault: true,
22326             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22327         });
22328     //this.addSeparator();
22329         this.loading = this.navgroup.addItem({
22330             tooltip: this.refreshText,
22331             icon: 'fa fa-refresh',
22332             preventDefault: true,
22333             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22334         });
22335         
22336     },
22337
22338     // private
22339     updateInfo : function(){
22340         if(this.displayEl){
22341             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22342             var msg = count == 0 ?
22343                 this.emptyMsg :
22344                 String.format(
22345                     this.displayMsg,
22346                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22347                 );
22348             this.displayEl.update(msg);
22349         }
22350     },
22351
22352     // private
22353     onLoad : function(ds, r, o){
22354        this.cursor = o.params ? o.params.start : 0;
22355        var d = this.getPageData(),
22356             ap = d.activePage,
22357             ps = d.pages;
22358         
22359        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22360        this.field.dom.value = ap;
22361        this.first.setDisabled(ap == 1);
22362        this.prev.setDisabled(ap == 1);
22363        this.next.setDisabled(ap == ps);
22364        this.last.setDisabled(ap == ps);
22365        this.loading.enable();
22366        this.updateInfo();
22367     },
22368
22369     // private
22370     getPageData : function(){
22371         var total = this.ds.getTotalCount();
22372         return {
22373             total : total,
22374             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22375             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22376         };
22377     },
22378
22379     // private
22380     onLoadError : function(){
22381         this.loading.enable();
22382     },
22383
22384     // private
22385     onPagingKeydown : function(e){
22386         var k = e.getKey();
22387         var d = this.getPageData();
22388         if(k == e.RETURN){
22389             var v = this.field.dom.value, pageNum;
22390             if(!v || isNaN(pageNum = parseInt(v, 10))){
22391                 this.field.dom.value = d.activePage;
22392                 return;
22393             }
22394             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22395             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22396             e.stopEvent();
22397         }
22398         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))
22399         {
22400           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22401           this.field.dom.value = pageNum;
22402           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22403           e.stopEvent();
22404         }
22405         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22406         {
22407           var v = this.field.dom.value, pageNum; 
22408           var increment = (e.shiftKey) ? 10 : 1;
22409           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22410                 increment *= -1;
22411           }
22412           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22413             this.field.dom.value = d.activePage;
22414             return;
22415           }
22416           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22417           {
22418             this.field.dom.value = parseInt(v, 10) + increment;
22419             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22420             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22421           }
22422           e.stopEvent();
22423         }
22424     },
22425
22426     // private
22427     beforeLoad : function(){
22428         if(this.loading){
22429             this.loading.disable();
22430         }
22431     },
22432
22433     // private
22434     onClick : function(which){
22435         
22436         var ds = this.ds;
22437         if (!ds) {
22438             return;
22439         }
22440         
22441         switch(which){
22442             case "first":
22443                 ds.load({params:{start: 0, limit: this.pageSize}});
22444             break;
22445             case "prev":
22446                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22447             break;
22448             case "next":
22449                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22450             break;
22451             case "last":
22452                 var total = ds.getTotalCount();
22453                 var extra = total % this.pageSize;
22454                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22455                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22456             break;
22457             case "refresh":
22458                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22459             break;
22460         }
22461     },
22462
22463     /**
22464      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22465      * @param {Roo.data.Store} store The data store to unbind
22466      */
22467     unbind : function(ds){
22468         ds.un("beforeload", this.beforeLoad, this);
22469         ds.un("load", this.onLoad, this);
22470         ds.un("loadexception", this.onLoadError, this);
22471         ds.un("remove", this.updateInfo, this);
22472         ds.un("add", this.updateInfo, this);
22473         this.ds = undefined;
22474     },
22475
22476     /**
22477      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22478      * @param {Roo.data.Store} store The data store to bind
22479      */
22480     bind : function(ds){
22481         ds.on("beforeload", this.beforeLoad, this);
22482         ds.on("load", this.onLoad, this);
22483         ds.on("loadexception", this.onLoadError, this);
22484         ds.on("remove", this.updateInfo, this);
22485         ds.on("add", this.updateInfo, this);
22486         this.ds = ds;
22487     }
22488 });/*
22489  * - LGPL
22490  *
22491  * element
22492  * 
22493  */
22494
22495 /**
22496  * @class Roo.bootstrap.MessageBar
22497  * @extends Roo.bootstrap.Component
22498  * Bootstrap MessageBar class
22499  * @cfg {String} html contents of the MessageBar
22500  * @cfg {String} weight (info | success | warning | danger) default info
22501  * @cfg {String} beforeClass insert the bar before the given class
22502  * @cfg {Boolean} closable (true | false) default false
22503  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22504  * 
22505  * @constructor
22506  * Create a new Element
22507  * @param {Object} config The config object
22508  */
22509
22510 Roo.bootstrap.MessageBar = function(config){
22511     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22512 };
22513
22514 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22515     
22516     html: '',
22517     weight: 'info',
22518     closable: false,
22519     fixed: false,
22520     beforeClass: 'bootstrap-sticky-wrap',
22521     
22522     getAutoCreate : function(){
22523         
22524         var cfg = {
22525             tag: 'div',
22526             cls: 'alert alert-dismissable alert-' + this.weight,
22527             cn: [
22528                 {
22529                     tag: 'span',
22530                     cls: 'message',
22531                     html: this.html || ''
22532                 }
22533             ]
22534         };
22535         
22536         if(this.fixed){
22537             cfg.cls += ' alert-messages-fixed';
22538         }
22539         
22540         if(this.closable){
22541             cfg.cn.push({
22542                 tag: 'button',
22543                 cls: 'close',
22544                 html: 'x'
22545             });
22546         }
22547         
22548         return cfg;
22549     },
22550     
22551     onRender : function(ct, position)
22552     {
22553         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22554         
22555         if(!this.el){
22556             var cfg = Roo.apply({},  this.getAutoCreate());
22557             cfg.id = Roo.id();
22558             
22559             if (this.cls) {
22560                 cfg.cls += ' ' + this.cls;
22561             }
22562             if (this.style) {
22563                 cfg.style = this.style;
22564             }
22565             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22566             
22567             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22568         }
22569         
22570         this.el.select('>button.close').on('click', this.hide, this);
22571         
22572     },
22573     
22574     show : function()
22575     {
22576         if (!this.rendered) {
22577             this.render();
22578         }
22579         
22580         this.el.show();
22581         
22582         this.fireEvent('show', this);
22583         
22584     },
22585     
22586     hide : function()
22587     {
22588         if (!this.rendered) {
22589             this.render();
22590         }
22591         
22592         this.el.hide();
22593         
22594         this.fireEvent('hide', this);
22595     },
22596     
22597     update : function()
22598     {
22599 //        var e = this.el.dom.firstChild;
22600 //        
22601 //        if(this.closable){
22602 //            e = e.nextSibling;
22603 //        }
22604 //        
22605 //        e.data = this.html || '';
22606
22607         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22608     }
22609    
22610 });
22611
22612  
22613
22614      /*
22615  * - LGPL
22616  *
22617  * Graph
22618  * 
22619  */
22620
22621
22622 /**
22623  * @class Roo.bootstrap.Graph
22624  * @extends Roo.bootstrap.Component
22625  * Bootstrap Graph class
22626 > Prameters
22627  -sm {number} sm 4
22628  -md {number} md 5
22629  @cfg {String} graphtype  bar | vbar | pie
22630  @cfg {number} g_x coodinator | centre x (pie)
22631  @cfg {number} g_y coodinator | centre y (pie)
22632  @cfg {number} g_r radius (pie)
22633  @cfg {number} g_height height of the chart (respected by all elements in the set)
22634  @cfg {number} g_width width of the chart (respected by all elements in the set)
22635  @cfg {Object} title The title of the chart
22636     
22637  -{Array}  values
22638  -opts (object) options for the chart 
22639      o {
22640      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22641      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22642      o vgutter (number)
22643      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.
22644      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22645      o to
22646      o stretch (boolean)
22647      o }
22648  -opts (object) options for the pie
22649      o{
22650      o cut
22651      o startAngle (number)
22652      o endAngle (number)
22653      } 
22654  *
22655  * @constructor
22656  * Create a new Input
22657  * @param {Object} config The config object
22658  */
22659
22660 Roo.bootstrap.Graph = function(config){
22661     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22662     
22663     this.addEvents({
22664         // img events
22665         /**
22666          * @event click
22667          * The img click event for the img.
22668          * @param {Roo.EventObject} e
22669          */
22670         "click" : true
22671     });
22672 };
22673
22674 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22675     
22676     sm: 4,
22677     md: 5,
22678     graphtype: 'bar',
22679     g_height: 250,
22680     g_width: 400,
22681     g_x: 50,
22682     g_y: 50,
22683     g_r: 30,
22684     opts:{
22685         //g_colors: this.colors,
22686         g_type: 'soft',
22687         g_gutter: '20%'
22688
22689     },
22690     title : false,
22691
22692     getAutoCreate : function(){
22693         
22694         var cfg = {
22695             tag: 'div',
22696             html : null
22697         };
22698         
22699         
22700         return  cfg;
22701     },
22702
22703     onRender : function(ct,position){
22704         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22705         this.raphael = Raphael(this.el.dom);
22706         
22707                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22708                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22709                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22710                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22711                 /*
22712                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22713                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22714                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22715                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22716                 
22717                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22718                 r.barchart(330, 10, 300, 220, data1);
22719                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22720                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22721                 */
22722                 
22723                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22724                 // r.barchart(30, 30, 560, 250,  xdata, {
22725                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22726                 //     axis : "0 0 1 1",
22727                 //     axisxlabels :  xdata
22728                 //     //yvalues : cols,
22729                    
22730                 // });
22731 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22732 //        
22733 //        this.load(null,xdata,{
22734 //                axis : "0 0 1 1",
22735 //                axisxlabels :  xdata
22736 //                });
22737
22738     },
22739
22740     load : function(graphtype,xdata,opts){
22741         this.raphael.clear();
22742         if(!graphtype) {
22743             graphtype = this.graphtype;
22744         }
22745         if(!opts){
22746             opts = this.opts;
22747         }
22748         var r = this.raphael,
22749             fin = function () {
22750                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22751             },
22752             fout = function () {
22753                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22754             },
22755             pfin = function() {
22756                 this.sector.stop();
22757                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22758
22759                 if (this.label) {
22760                     this.label[0].stop();
22761                     this.label[0].attr({ r: 7.5 });
22762                     this.label[1].attr({ "font-weight": 800 });
22763                 }
22764             },
22765             pfout = function() {
22766                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22767
22768                 if (this.label) {
22769                     this.label[0].animate({ r: 5 }, 500, "bounce");
22770                     this.label[1].attr({ "font-weight": 400 });
22771                 }
22772             };
22773
22774         switch(graphtype){
22775             case 'bar':
22776                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22777                 break;
22778             case 'hbar':
22779                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22780                 break;
22781             case 'pie':
22782 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22783 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22784 //            
22785                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22786                 
22787                 break;
22788
22789         }
22790         
22791         if(this.title){
22792             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22793         }
22794         
22795     },
22796     
22797     setTitle: function(o)
22798     {
22799         this.title = o;
22800     },
22801     
22802     initEvents: function() {
22803         
22804         if(!this.href){
22805             this.el.on('click', this.onClick, this);
22806         }
22807     },
22808     
22809     onClick : function(e)
22810     {
22811         Roo.log('img onclick');
22812         this.fireEvent('click', this, e);
22813     }
22814    
22815 });
22816
22817  
22818 /*
22819  * - LGPL
22820  *
22821  * numberBox
22822  * 
22823  */
22824 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22825
22826 /**
22827  * @class Roo.bootstrap.dash.NumberBox
22828  * @extends Roo.bootstrap.Component
22829  * Bootstrap NumberBox class
22830  * @cfg {String} headline Box headline
22831  * @cfg {String} content Box content
22832  * @cfg {String} icon Box icon
22833  * @cfg {String} footer Footer text
22834  * @cfg {String} fhref Footer href
22835  * 
22836  * @constructor
22837  * Create a new NumberBox
22838  * @param {Object} config The config object
22839  */
22840
22841
22842 Roo.bootstrap.dash.NumberBox = function(config){
22843     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22844     
22845 };
22846
22847 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22848     
22849     headline : '',
22850     content : '',
22851     icon : '',
22852     footer : '',
22853     fhref : '',
22854     ficon : '',
22855     
22856     getAutoCreate : function(){
22857         
22858         var cfg = {
22859             tag : 'div',
22860             cls : 'small-box ',
22861             cn : [
22862                 {
22863                     tag : 'div',
22864                     cls : 'inner',
22865                     cn :[
22866                         {
22867                             tag : 'h3',
22868                             cls : 'roo-headline',
22869                             html : this.headline
22870                         },
22871                         {
22872                             tag : 'p',
22873                             cls : 'roo-content',
22874                             html : this.content
22875                         }
22876                     ]
22877                 }
22878             ]
22879         };
22880         
22881         if(this.icon){
22882             cfg.cn.push({
22883                 tag : 'div',
22884                 cls : 'icon',
22885                 cn :[
22886                     {
22887                         tag : 'i',
22888                         cls : 'ion ' + this.icon
22889                     }
22890                 ]
22891             });
22892         }
22893         
22894         if(this.footer){
22895             var footer = {
22896                 tag : 'a',
22897                 cls : 'small-box-footer',
22898                 href : this.fhref || '#',
22899                 html : this.footer
22900             };
22901             
22902             cfg.cn.push(footer);
22903             
22904         }
22905         
22906         return  cfg;
22907     },
22908
22909     onRender : function(ct,position){
22910         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22911
22912
22913        
22914                 
22915     },
22916
22917     setHeadline: function (value)
22918     {
22919         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22920     },
22921     
22922     setFooter: function (value, href)
22923     {
22924         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
22925         
22926         if(href){
22927             this.el.select('a.small-box-footer',true).first().attr('href', href);
22928         }
22929         
22930     },
22931
22932     setContent: function (value)
22933     {
22934         this.el.select('.roo-content',true).first().dom.innerHTML = value;
22935     },
22936
22937     initEvents: function() 
22938     {   
22939         
22940     }
22941     
22942 });
22943
22944  
22945 /*
22946  * - LGPL
22947  *
22948  * TabBox
22949  * 
22950  */
22951 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22952
22953 /**
22954  * @class Roo.bootstrap.dash.TabBox
22955  * @extends Roo.bootstrap.Component
22956  * Bootstrap TabBox class
22957  * @cfg {String} title Title of the TabBox
22958  * @cfg {String} icon Icon of the TabBox
22959  * @cfg {Boolean} showtabs (true|false) show the tabs default true
22960  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
22961  * 
22962  * @constructor
22963  * Create a new TabBox
22964  * @param {Object} config The config object
22965  */
22966
22967
22968 Roo.bootstrap.dash.TabBox = function(config){
22969     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
22970     this.addEvents({
22971         // raw events
22972         /**
22973          * @event addpane
22974          * When a pane is added
22975          * @param {Roo.bootstrap.dash.TabPane} pane
22976          */
22977         "addpane" : true,
22978         /**
22979          * @event activatepane
22980          * When a pane is activated
22981          * @param {Roo.bootstrap.dash.TabPane} pane
22982          */
22983         "activatepane" : true
22984         
22985          
22986     });
22987     
22988     this.panes = [];
22989 };
22990
22991 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
22992
22993     title : '',
22994     icon : false,
22995     showtabs : true,
22996     tabScrollable : false,
22997     
22998     getChildContainer : function()
22999     {
23000         return this.el.select('.tab-content', true).first();
23001     },
23002     
23003     getAutoCreate : function(){
23004         
23005         var header = {
23006             tag: 'li',
23007             cls: 'pull-left header',
23008             html: this.title,
23009             cn : []
23010         };
23011         
23012         if(this.icon){
23013             header.cn.push({
23014                 tag: 'i',
23015                 cls: 'fa ' + this.icon
23016             });
23017         }
23018         
23019         var h = {
23020             tag: 'ul',
23021             cls: 'nav nav-tabs pull-right',
23022             cn: [
23023                 header
23024             ]
23025         };
23026         
23027         if(this.tabScrollable){
23028             h = {
23029                 tag: 'div',
23030                 cls: 'tab-header',
23031                 cn: [
23032                     {
23033                         tag: 'ul',
23034                         cls: 'nav nav-tabs pull-right',
23035                         cn: [
23036                             header
23037                         ]
23038                     }
23039                 ]
23040             };
23041         }
23042         
23043         var cfg = {
23044             tag: 'div',
23045             cls: 'nav-tabs-custom',
23046             cn: [
23047                 h,
23048                 {
23049                     tag: 'div',
23050                     cls: 'tab-content no-padding',
23051                     cn: []
23052                 }
23053             ]
23054         };
23055
23056         return  cfg;
23057     },
23058     initEvents : function()
23059     {
23060         //Roo.log('add add pane handler');
23061         this.on('addpane', this.onAddPane, this);
23062     },
23063      /**
23064      * Updates the box title
23065      * @param {String} html to set the title to.
23066      */
23067     setTitle : function(value)
23068     {
23069         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23070     },
23071     onAddPane : function(pane)
23072     {
23073         this.panes.push(pane);
23074         //Roo.log('addpane');
23075         //Roo.log(pane);
23076         // tabs are rendere left to right..
23077         if(!this.showtabs){
23078             return;
23079         }
23080         
23081         var ctr = this.el.select('.nav-tabs', true).first();
23082          
23083          
23084         var existing = ctr.select('.nav-tab',true);
23085         var qty = existing.getCount();;
23086         
23087         
23088         var tab = ctr.createChild({
23089             tag : 'li',
23090             cls : 'nav-tab' + (qty ? '' : ' active'),
23091             cn : [
23092                 {
23093                     tag : 'a',
23094                     href:'#',
23095                     html : pane.title
23096                 }
23097             ]
23098         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23099         pane.tab = tab;
23100         
23101         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23102         if (!qty) {
23103             pane.el.addClass('active');
23104         }
23105         
23106                 
23107     },
23108     onTabClick : function(ev,un,ob,pane)
23109     {
23110         //Roo.log('tab - prev default');
23111         ev.preventDefault();
23112         
23113         
23114         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23115         pane.tab.addClass('active');
23116         //Roo.log(pane.title);
23117         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23118         // technically we should have a deactivate event.. but maybe add later.
23119         // and it should not de-activate the selected tab...
23120         this.fireEvent('activatepane', pane);
23121         pane.el.addClass('active');
23122         pane.fireEvent('activate');
23123         
23124         
23125     },
23126     
23127     getActivePane : function()
23128     {
23129         var r = false;
23130         Roo.each(this.panes, function(p) {
23131             if(p.el.hasClass('active')){
23132                 r = p;
23133                 return false;
23134             }
23135             
23136             return;
23137         });
23138         
23139         return r;
23140     }
23141     
23142     
23143 });
23144
23145  
23146 /*
23147  * - LGPL
23148  *
23149  * Tab pane
23150  * 
23151  */
23152 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23153 /**
23154  * @class Roo.bootstrap.TabPane
23155  * @extends Roo.bootstrap.Component
23156  * Bootstrap TabPane class
23157  * @cfg {Boolean} active (false | true) Default false
23158  * @cfg {String} title title of panel
23159
23160  * 
23161  * @constructor
23162  * Create a new TabPane
23163  * @param {Object} config The config object
23164  */
23165
23166 Roo.bootstrap.dash.TabPane = function(config){
23167     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23168     
23169     this.addEvents({
23170         // raw events
23171         /**
23172          * @event activate
23173          * When a pane is activated
23174          * @param {Roo.bootstrap.dash.TabPane} pane
23175          */
23176         "activate" : true
23177          
23178     });
23179 };
23180
23181 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23182     
23183     active : false,
23184     title : '',
23185     
23186     // the tabBox that this is attached to.
23187     tab : false,
23188      
23189     getAutoCreate : function() 
23190     {
23191         var cfg = {
23192             tag: 'div',
23193             cls: 'tab-pane'
23194         };
23195         
23196         if(this.active){
23197             cfg.cls += ' active';
23198         }
23199         
23200         return cfg;
23201     },
23202     initEvents  : function()
23203     {
23204         //Roo.log('trigger add pane handler');
23205         this.parent().fireEvent('addpane', this)
23206     },
23207     
23208      /**
23209      * Updates the tab title 
23210      * @param {String} html to set the title to.
23211      */
23212     setTitle: function(str)
23213     {
23214         if (!this.tab) {
23215             return;
23216         }
23217         this.title = str;
23218         this.tab.select('a', true).first().dom.innerHTML = str;
23219         
23220     }
23221     
23222     
23223     
23224 });
23225
23226  
23227
23228
23229  /*
23230  * - LGPL
23231  *
23232  * menu
23233  * 
23234  */
23235 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23236
23237 /**
23238  * @class Roo.bootstrap.menu.Menu
23239  * @extends Roo.bootstrap.Component
23240  * Bootstrap Menu class - container for Menu
23241  * @cfg {String} html Text of the menu
23242  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23243  * @cfg {String} icon Font awesome icon
23244  * @cfg {String} pos Menu align to (top | bottom) default bottom
23245  * 
23246  * 
23247  * @constructor
23248  * Create a new Menu
23249  * @param {Object} config The config object
23250  */
23251
23252
23253 Roo.bootstrap.menu.Menu = function(config){
23254     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23255     
23256     this.addEvents({
23257         /**
23258          * @event beforeshow
23259          * Fires before this menu is displayed
23260          * @param {Roo.bootstrap.menu.Menu} this
23261          */
23262         beforeshow : true,
23263         /**
23264          * @event beforehide
23265          * Fires before this menu is hidden
23266          * @param {Roo.bootstrap.menu.Menu} this
23267          */
23268         beforehide : true,
23269         /**
23270          * @event show
23271          * Fires after this menu is displayed
23272          * @param {Roo.bootstrap.menu.Menu} this
23273          */
23274         show : true,
23275         /**
23276          * @event hide
23277          * Fires after this menu is hidden
23278          * @param {Roo.bootstrap.menu.Menu} this
23279          */
23280         hide : true,
23281         /**
23282          * @event click
23283          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23284          * @param {Roo.bootstrap.menu.Menu} this
23285          * @param {Roo.EventObject} e
23286          */
23287         click : true
23288     });
23289     
23290 };
23291
23292 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23293     
23294     submenu : false,
23295     html : '',
23296     weight : 'default',
23297     icon : false,
23298     pos : 'bottom',
23299     
23300     
23301     getChildContainer : function() {
23302         if(this.isSubMenu){
23303             return this.el;
23304         }
23305         
23306         return this.el.select('ul.dropdown-menu', true).first();  
23307     },
23308     
23309     getAutoCreate : function()
23310     {
23311         var text = [
23312             {
23313                 tag : 'span',
23314                 cls : 'roo-menu-text',
23315                 html : this.html
23316             }
23317         ];
23318         
23319         if(this.icon){
23320             text.unshift({
23321                 tag : 'i',
23322                 cls : 'fa ' + this.icon
23323             })
23324         }
23325         
23326         
23327         var cfg = {
23328             tag : 'div',
23329             cls : 'btn-group',
23330             cn : [
23331                 {
23332                     tag : 'button',
23333                     cls : 'dropdown-button btn btn-' + this.weight,
23334                     cn : text
23335                 },
23336                 {
23337                     tag : 'button',
23338                     cls : 'dropdown-toggle btn btn-' + this.weight,
23339                     cn : [
23340                         {
23341                             tag : 'span',
23342                             cls : 'caret'
23343                         }
23344                     ]
23345                 },
23346                 {
23347                     tag : 'ul',
23348                     cls : 'dropdown-menu'
23349                 }
23350             ]
23351             
23352         };
23353         
23354         if(this.pos == 'top'){
23355             cfg.cls += ' dropup';
23356         }
23357         
23358         if(this.isSubMenu){
23359             cfg = {
23360                 tag : 'ul',
23361                 cls : 'dropdown-menu'
23362             }
23363         }
23364         
23365         return cfg;
23366     },
23367     
23368     onRender : function(ct, position)
23369     {
23370         this.isSubMenu = ct.hasClass('dropdown-submenu');
23371         
23372         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23373     },
23374     
23375     initEvents : function() 
23376     {
23377         if(this.isSubMenu){
23378             return;
23379         }
23380         
23381         this.hidden = true;
23382         
23383         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23384         this.triggerEl.on('click', this.onTriggerPress, this);
23385         
23386         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23387         this.buttonEl.on('click', this.onClick, this);
23388         
23389     },
23390     
23391     list : function()
23392     {
23393         if(this.isSubMenu){
23394             return this.el;
23395         }
23396         
23397         return this.el.select('ul.dropdown-menu', true).first();
23398     },
23399     
23400     onClick : function(e)
23401     {
23402         this.fireEvent("click", this, e);
23403     },
23404     
23405     onTriggerPress  : function(e)
23406     {   
23407         if (this.isVisible()) {
23408             this.hide();
23409         } else {
23410             this.show();
23411         }
23412     },
23413     
23414     isVisible : function(){
23415         return !this.hidden;
23416     },
23417     
23418     show : function()
23419     {
23420         this.fireEvent("beforeshow", this);
23421         
23422         this.hidden = false;
23423         this.el.addClass('open');
23424         
23425         Roo.get(document).on("mouseup", this.onMouseUp, this);
23426         
23427         this.fireEvent("show", this);
23428         
23429         
23430     },
23431     
23432     hide : function()
23433     {
23434         this.fireEvent("beforehide", this);
23435         
23436         this.hidden = true;
23437         this.el.removeClass('open');
23438         
23439         Roo.get(document).un("mouseup", this.onMouseUp);
23440         
23441         this.fireEvent("hide", this);
23442     },
23443     
23444     onMouseUp : function()
23445     {
23446         this.hide();
23447     }
23448     
23449 });
23450
23451  
23452  /*
23453  * - LGPL
23454  *
23455  * menu item
23456  * 
23457  */
23458 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23459
23460 /**
23461  * @class Roo.bootstrap.menu.Item
23462  * @extends Roo.bootstrap.Component
23463  * Bootstrap MenuItem class
23464  * @cfg {Boolean} submenu (true | false) default false
23465  * @cfg {String} html text of the item
23466  * @cfg {String} href the link
23467  * @cfg {Boolean} disable (true | false) default false
23468  * @cfg {Boolean} preventDefault (true | false) default true
23469  * @cfg {String} icon Font awesome icon
23470  * @cfg {String} pos Submenu align to (left | right) default right 
23471  * 
23472  * 
23473  * @constructor
23474  * Create a new Item
23475  * @param {Object} config The config object
23476  */
23477
23478
23479 Roo.bootstrap.menu.Item = function(config){
23480     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23481     this.addEvents({
23482         /**
23483          * @event mouseover
23484          * Fires when the mouse is hovering over this menu
23485          * @param {Roo.bootstrap.menu.Item} this
23486          * @param {Roo.EventObject} e
23487          */
23488         mouseover : true,
23489         /**
23490          * @event mouseout
23491          * Fires when the mouse exits this menu
23492          * @param {Roo.bootstrap.menu.Item} this
23493          * @param {Roo.EventObject} e
23494          */
23495         mouseout : true,
23496         // raw events
23497         /**
23498          * @event click
23499          * The raw click event for the entire grid.
23500          * @param {Roo.EventObject} e
23501          */
23502         click : true
23503     });
23504 };
23505
23506 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23507     
23508     submenu : false,
23509     href : '',
23510     html : '',
23511     preventDefault: true,
23512     disable : false,
23513     icon : false,
23514     pos : 'right',
23515     
23516     getAutoCreate : function()
23517     {
23518         var text = [
23519             {
23520                 tag : 'span',
23521                 cls : 'roo-menu-item-text',
23522                 html : this.html
23523             }
23524         ];
23525         
23526         if(this.icon){
23527             text.unshift({
23528                 tag : 'i',
23529                 cls : 'fa ' + this.icon
23530             })
23531         }
23532         
23533         var cfg = {
23534             tag : 'li',
23535             cn : [
23536                 {
23537                     tag : 'a',
23538                     href : this.href || '#',
23539                     cn : text
23540                 }
23541             ]
23542         };
23543         
23544         if(this.disable){
23545             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23546         }
23547         
23548         if(this.submenu){
23549             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23550             
23551             if(this.pos == 'left'){
23552                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23553             }
23554         }
23555         
23556         return cfg;
23557     },
23558     
23559     initEvents : function() 
23560     {
23561         this.el.on('mouseover', this.onMouseOver, this);
23562         this.el.on('mouseout', this.onMouseOut, this);
23563         
23564         this.el.select('a', true).first().on('click', this.onClick, this);
23565         
23566     },
23567     
23568     onClick : function(e)
23569     {
23570         if(this.preventDefault){
23571             e.preventDefault();
23572         }
23573         
23574         this.fireEvent("click", this, e);
23575     },
23576     
23577     onMouseOver : function(e)
23578     {
23579         if(this.submenu && this.pos == 'left'){
23580             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23581         }
23582         
23583         this.fireEvent("mouseover", this, e);
23584     },
23585     
23586     onMouseOut : function(e)
23587     {
23588         this.fireEvent("mouseout", this, e);
23589     }
23590 });
23591
23592  
23593
23594  /*
23595  * - LGPL
23596  *
23597  * menu separator
23598  * 
23599  */
23600 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23601
23602 /**
23603  * @class Roo.bootstrap.menu.Separator
23604  * @extends Roo.bootstrap.Component
23605  * Bootstrap Separator class
23606  * 
23607  * @constructor
23608  * Create a new Separator
23609  * @param {Object} config The config object
23610  */
23611
23612
23613 Roo.bootstrap.menu.Separator = function(config){
23614     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23615 };
23616
23617 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23618     
23619     getAutoCreate : function(){
23620         var cfg = {
23621             tag : 'li',
23622             cls: 'divider'
23623         };
23624         
23625         return cfg;
23626     }
23627    
23628 });
23629
23630  
23631
23632  /*
23633  * - LGPL
23634  *
23635  * Tooltip
23636  * 
23637  */
23638
23639 /**
23640  * @class Roo.bootstrap.Tooltip
23641  * Bootstrap Tooltip class
23642  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23643  * to determine which dom element triggers the tooltip.
23644  * 
23645  * It needs to add support for additional attributes like tooltip-position
23646  * 
23647  * @constructor
23648  * Create a new Toolti
23649  * @param {Object} config The config object
23650  */
23651
23652 Roo.bootstrap.Tooltip = function(config){
23653     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23654 };
23655
23656 Roo.apply(Roo.bootstrap.Tooltip, {
23657     /**
23658      * @function init initialize tooltip monitoring.
23659      * @static
23660      */
23661     currentEl : false,
23662     currentTip : false,
23663     currentRegion : false,
23664     
23665     //  init : delay?
23666     
23667     init : function()
23668     {
23669         Roo.get(document).on('mouseover', this.enter ,this);
23670         Roo.get(document).on('mouseout', this.leave, this);
23671          
23672         
23673         this.currentTip = new Roo.bootstrap.Tooltip();
23674     },
23675     
23676     enter : function(ev)
23677     {
23678         var dom = ev.getTarget();
23679         
23680         //Roo.log(['enter',dom]);
23681         var el = Roo.fly(dom);
23682         if (this.currentEl) {
23683             //Roo.log(dom);
23684             //Roo.log(this.currentEl);
23685             //Roo.log(this.currentEl.contains(dom));
23686             if (this.currentEl == el) {
23687                 return;
23688             }
23689             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23690                 return;
23691             }
23692
23693         }
23694         
23695         if (this.currentTip.el) {
23696             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23697         }    
23698         //Roo.log(ev);
23699         var bindEl = el;
23700         
23701         // you can not look for children, as if el is the body.. then everythign is the child..
23702         if (!el.attr('tooltip')) { //
23703             if (!el.select("[tooltip]").elements.length) {
23704                 return;
23705             }
23706             // is the mouse over this child...?
23707             bindEl = el.select("[tooltip]").first();
23708             var xy = ev.getXY();
23709             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23710                 //Roo.log("not in region.");
23711                 return;
23712             }
23713             //Roo.log("child element over..");
23714             
23715         }
23716         this.currentEl = bindEl;
23717         this.currentTip.bind(bindEl);
23718         this.currentRegion = Roo.lib.Region.getRegion(dom);
23719         this.currentTip.enter();
23720         
23721     },
23722     leave : function(ev)
23723     {
23724         var dom = ev.getTarget();
23725         //Roo.log(['leave',dom]);
23726         if (!this.currentEl) {
23727             return;
23728         }
23729         
23730         
23731         if (dom != this.currentEl.dom) {
23732             return;
23733         }
23734         var xy = ev.getXY();
23735         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23736             return;
23737         }
23738         // only activate leave if mouse cursor is outside... bounding box..
23739         
23740         
23741         
23742         
23743         if (this.currentTip) {
23744             this.currentTip.leave();
23745         }
23746         //Roo.log('clear currentEl');
23747         this.currentEl = false;
23748         
23749         
23750     },
23751     alignment : {
23752         'left' : ['r-l', [-2,0], 'right'],
23753         'right' : ['l-r', [2,0], 'left'],
23754         'bottom' : ['t-b', [0,2], 'top'],
23755         'top' : [ 'b-t', [0,-2], 'bottom']
23756     }
23757     
23758 });
23759
23760
23761 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23762     
23763     
23764     bindEl : false,
23765     
23766     delay : null, // can be { show : 300 , hide: 500}
23767     
23768     timeout : null,
23769     
23770     hoverState : null, //???
23771     
23772     placement : 'bottom', 
23773     
23774     getAutoCreate : function(){
23775     
23776         var cfg = {
23777            cls : 'tooltip',
23778            role : 'tooltip',
23779            cn : [
23780                 {
23781                     cls : 'tooltip-arrow'
23782                 },
23783                 {
23784                     cls : 'tooltip-inner'
23785                 }
23786            ]
23787         };
23788         
23789         return cfg;
23790     },
23791     bind : function(el)
23792     {
23793         this.bindEl = el;
23794     },
23795       
23796     
23797     enter : function () {
23798        
23799         if (this.timeout != null) {
23800             clearTimeout(this.timeout);
23801         }
23802         
23803         this.hoverState = 'in';
23804          //Roo.log("enter - show");
23805         if (!this.delay || !this.delay.show) {
23806             this.show();
23807             return;
23808         }
23809         var _t = this;
23810         this.timeout = setTimeout(function () {
23811             if (_t.hoverState == 'in') {
23812                 _t.show();
23813             }
23814         }, this.delay.show);
23815     },
23816     leave : function()
23817     {
23818         clearTimeout(this.timeout);
23819     
23820         this.hoverState = 'out';
23821          if (!this.delay || !this.delay.hide) {
23822             this.hide();
23823             return;
23824         }
23825        
23826         var _t = this;
23827         this.timeout = setTimeout(function () {
23828             //Roo.log("leave - timeout");
23829             
23830             if (_t.hoverState == 'out') {
23831                 _t.hide();
23832                 Roo.bootstrap.Tooltip.currentEl = false;
23833             }
23834         }, delay);
23835     },
23836     
23837     show : function ()
23838     {
23839         if (!this.el) {
23840             this.render(document.body);
23841         }
23842         // set content.
23843         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23844         
23845         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23846         
23847         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23848         
23849         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23850         
23851         var placement = typeof this.placement == 'function' ?
23852             this.placement.call(this, this.el, on_el) :
23853             this.placement;
23854             
23855         var autoToken = /\s?auto?\s?/i;
23856         var autoPlace = autoToken.test(placement);
23857         if (autoPlace) {
23858             placement = placement.replace(autoToken, '') || 'top';
23859         }
23860         
23861         //this.el.detach()
23862         //this.el.setXY([0,0]);
23863         this.el.show();
23864         //this.el.dom.style.display='block';
23865         
23866         //this.el.appendTo(on_el);
23867         
23868         var p = this.getPosition();
23869         var box = this.el.getBox();
23870         
23871         if (autoPlace) {
23872             // fixme..
23873         }
23874         
23875         var align = Roo.bootstrap.Tooltip.alignment[placement];
23876         
23877         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23878         
23879         if(placement == 'top' || placement == 'bottom'){
23880             if(xy[0] < 0){
23881                 placement = 'right';
23882             }
23883             
23884             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23885                 placement = 'left';
23886             }
23887         }
23888         
23889         align = Roo.bootstrap.Tooltip.alignment[placement];
23890         
23891         this.el.alignTo(this.bindEl, align[0],align[1]);
23892         //var arrow = this.el.select('.arrow',true).first();
23893         //arrow.set(align[2], 
23894         
23895         this.el.addClass(placement);
23896         
23897         this.el.addClass('in fade');
23898         
23899         this.hoverState = null;
23900         
23901         if (this.el.hasClass('fade')) {
23902             // fade it?
23903         }
23904         
23905     },
23906     hide : function()
23907     {
23908          
23909         if (!this.el) {
23910             return;
23911         }
23912         //this.el.setXY([0,0]);
23913         this.el.removeClass('in');
23914         //this.el.hide();
23915         
23916     }
23917     
23918 });
23919  
23920
23921  /*
23922  * - LGPL
23923  *
23924  * Location Picker
23925  * 
23926  */
23927
23928 /**
23929  * @class Roo.bootstrap.LocationPicker
23930  * @extends Roo.bootstrap.Component
23931  * Bootstrap LocationPicker class
23932  * @cfg {Number} latitude Position when init default 0
23933  * @cfg {Number} longitude Position when init default 0
23934  * @cfg {Number} zoom default 15
23935  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
23936  * @cfg {Boolean} mapTypeControl default false
23937  * @cfg {Boolean} disableDoubleClickZoom default false
23938  * @cfg {Boolean} scrollwheel default true
23939  * @cfg {Boolean} streetViewControl default false
23940  * @cfg {Number} radius default 0
23941  * @cfg {String} locationName
23942  * @cfg {Boolean} draggable default true
23943  * @cfg {Boolean} enableAutocomplete default false
23944  * @cfg {Boolean} enableReverseGeocode default true
23945  * @cfg {String} markerTitle
23946  * 
23947  * @constructor
23948  * Create a new LocationPicker
23949  * @param {Object} config The config object
23950  */
23951
23952
23953 Roo.bootstrap.LocationPicker = function(config){
23954     
23955     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
23956     
23957     this.addEvents({
23958         /**
23959          * @event initial
23960          * Fires when the picker initialized.
23961          * @param {Roo.bootstrap.LocationPicker} this
23962          * @param {Google Location} location
23963          */
23964         initial : true,
23965         /**
23966          * @event positionchanged
23967          * Fires when the picker position changed.
23968          * @param {Roo.bootstrap.LocationPicker} this
23969          * @param {Google Location} location
23970          */
23971         positionchanged : true,
23972         /**
23973          * @event resize
23974          * Fires when the map resize.
23975          * @param {Roo.bootstrap.LocationPicker} this
23976          */
23977         resize : true,
23978         /**
23979          * @event show
23980          * Fires when the map show.
23981          * @param {Roo.bootstrap.LocationPicker} this
23982          */
23983         show : true,
23984         /**
23985          * @event hide
23986          * Fires when the map hide.
23987          * @param {Roo.bootstrap.LocationPicker} this
23988          */
23989         hide : true,
23990         /**
23991          * @event mapClick
23992          * Fires when click the map.
23993          * @param {Roo.bootstrap.LocationPicker} this
23994          * @param {Map event} e
23995          */
23996         mapClick : true,
23997         /**
23998          * @event mapRightClick
23999          * Fires when right click the map.
24000          * @param {Roo.bootstrap.LocationPicker} this
24001          * @param {Map event} e
24002          */
24003         mapRightClick : true,
24004         /**
24005          * @event markerClick
24006          * Fires when click the marker.
24007          * @param {Roo.bootstrap.LocationPicker} this
24008          * @param {Map event} e
24009          */
24010         markerClick : true,
24011         /**
24012          * @event markerRightClick
24013          * Fires when right click the marker.
24014          * @param {Roo.bootstrap.LocationPicker} this
24015          * @param {Map event} e
24016          */
24017         markerRightClick : true,
24018         /**
24019          * @event OverlayViewDraw
24020          * Fires when OverlayView Draw
24021          * @param {Roo.bootstrap.LocationPicker} this
24022          */
24023         OverlayViewDraw : true,
24024         /**
24025          * @event OverlayViewOnAdd
24026          * Fires when OverlayView Draw
24027          * @param {Roo.bootstrap.LocationPicker} this
24028          */
24029         OverlayViewOnAdd : true,
24030         /**
24031          * @event OverlayViewOnRemove
24032          * Fires when OverlayView Draw
24033          * @param {Roo.bootstrap.LocationPicker} this
24034          */
24035         OverlayViewOnRemove : true,
24036         /**
24037          * @event OverlayViewShow
24038          * Fires when OverlayView Draw
24039          * @param {Roo.bootstrap.LocationPicker} this
24040          * @param {Pixel} cpx
24041          */
24042         OverlayViewShow : true,
24043         /**
24044          * @event OverlayViewHide
24045          * Fires when OverlayView Draw
24046          * @param {Roo.bootstrap.LocationPicker} this
24047          */
24048         OverlayViewHide : true,
24049         /**
24050          * @event loadexception
24051          * Fires when load google lib failed.
24052          * @param {Roo.bootstrap.LocationPicker} this
24053          */
24054         loadexception : true
24055     });
24056         
24057 };
24058
24059 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24060     
24061     gMapContext: false,
24062     
24063     latitude: 0,
24064     longitude: 0,
24065     zoom: 15,
24066     mapTypeId: false,
24067     mapTypeControl: false,
24068     disableDoubleClickZoom: false,
24069     scrollwheel: true,
24070     streetViewControl: false,
24071     radius: 0,
24072     locationName: '',
24073     draggable: true,
24074     enableAutocomplete: false,
24075     enableReverseGeocode: true,
24076     markerTitle: '',
24077     
24078     getAutoCreate: function()
24079     {
24080
24081         var cfg = {
24082             tag: 'div',
24083             cls: 'roo-location-picker'
24084         };
24085         
24086         return cfg
24087     },
24088     
24089     initEvents: function(ct, position)
24090     {       
24091         if(!this.el.getWidth() || this.isApplied()){
24092             return;
24093         }
24094         
24095         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24096         
24097         this.initial();
24098     },
24099     
24100     initial: function()
24101     {
24102         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24103             this.fireEvent('loadexception', this);
24104             return;
24105         }
24106         
24107         if(!this.mapTypeId){
24108             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24109         }
24110         
24111         this.gMapContext = this.GMapContext();
24112         
24113         this.initOverlayView();
24114         
24115         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24116         
24117         var _this = this;
24118                 
24119         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24120             _this.setPosition(_this.gMapContext.marker.position);
24121         });
24122         
24123         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24124             _this.fireEvent('mapClick', this, event);
24125             
24126         });
24127
24128         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24129             _this.fireEvent('mapRightClick', this, event);
24130             
24131         });
24132         
24133         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24134             _this.fireEvent('markerClick', this, event);
24135             
24136         });
24137
24138         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24139             _this.fireEvent('markerRightClick', this, event);
24140             
24141         });
24142         
24143         this.setPosition(this.gMapContext.location);
24144         
24145         this.fireEvent('initial', this, this.gMapContext.location);
24146     },
24147     
24148     initOverlayView: function()
24149     {
24150         var _this = this;
24151         
24152         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24153             
24154             draw: function()
24155             {
24156                 _this.fireEvent('OverlayViewDraw', _this);
24157             },
24158             
24159             onAdd: function()
24160             {
24161                 _this.fireEvent('OverlayViewOnAdd', _this);
24162             },
24163             
24164             onRemove: function()
24165             {
24166                 _this.fireEvent('OverlayViewOnRemove', _this);
24167             },
24168             
24169             show: function(cpx)
24170             {
24171                 _this.fireEvent('OverlayViewShow', _this, cpx);
24172             },
24173             
24174             hide: function()
24175             {
24176                 _this.fireEvent('OverlayViewHide', _this);
24177             }
24178             
24179         });
24180     },
24181     
24182     fromLatLngToContainerPixel: function(event)
24183     {
24184         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24185     },
24186     
24187     isApplied: function() 
24188     {
24189         return this.getGmapContext() == false ? false : true;
24190     },
24191     
24192     getGmapContext: function() 
24193     {
24194         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24195     },
24196     
24197     GMapContext: function() 
24198     {
24199         var position = new google.maps.LatLng(this.latitude, this.longitude);
24200         
24201         var _map = new google.maps.Map(this.el.dom, {
24202             center: position,
24203             zoom: this.zoom,
24204             mapTypeId: this.mapTypeId,
24205             mapTypeControl: this.mapTypeControl,
24206             disableDoubleClickZoom: this.disableDoubleClickZoom,
24207             scrollwheel: this.scrollwheel,
24208             streetViewControl: this.streetViewControl,
24209             locationName: this.locationName,
24210             draggable: this.draggable,
24211             enableAutocomplete: this.enableAutocomplete,
24212             enableReverseGeocode: this.enableReverseGeocode
24213         });
24214         
24215         var _marker = new google.maps.Marker({
24216             position: position,
24217             map: _map,
24218             title: this.markerTitle,
24219             draggable: this.draggable
24220         });
24221         
24222         return {
24223             map: _map,
24224             marker: _marker,
24225             circle: null,
24226             location: position,
24227             radius: this.radius,
24228             locationName: this.locationName,
24229             addressComponents: {
24230                 formatted_address: null,
24231                 addressLine1: null,
24232                 addressLine2: null,
24233                 streetName: null,
24234                 streetNumber: null,
24235                 city: null,
24236                 district: null,
24237                 state: null,
24238                 stateOrProvince: null
24239             },
24240             settings: this,
24241             domContainer: this.el.dom,
24242             geodecoder: new google.maps.Geocoder()
24243         };
24244     },
24245     
24246     drawCircle: function(center, radius, options) 
24247     {
24248         if (this.gMapContext.circle != null) {
24249             this.gMapContext.circle.setMap(null);
24250         }
24251         if (radius > 0) {
24252             radius *= 1;
24253             options = Roo.apply({}, options, {
24254                 strokeColor: "#0000FF",
24255                 strokeOpacity: .35,
24256                 strokeWeight: 2,
24257                 fillColor: "#0000FF",
24258                 fillOpacity: .2
24259             });
24260             
24261             options.map = this.gMapContext.map;
24262             options.radius = radius;
24263             options.center = center;
24264             this.gMapContext.circle = new google.maps.Circle(options);
24265             return this.gMapContext.circle;
24266         }
24267         
24268         return null;
24269     },
24270     
24271     setPosition: function(location) 
24272     {
24273         this.gMapContext.location = location;
24274         this.gMapContext.marker.setPosition(location);
24275         this.gMapContext.map.panTo(location);
24276         this.drawCircle(location, this.gMapContext.radius, {});
24277         
24278         var _this = this;
24279         
24280         if (this.gMapContext.settings.enableReverseGeocode) {
24281             this.gMapContext.geodecoder.geocode({
24282                 latLng: this.gMapContext.location
24283             }, function(results, status) {
24284                 
24285                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24286                     _this.gMapContext.locationName = results[0].formatted_address;
24287                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24288                     
24289                     _this.fireEvent('positionchanged', this, location);
24290                 }
24291             });
24292             
24293             return;
24294         }
24295         
24296         this.fireEvent('positionchanged', this, location);
24297     },
24298     
24299     resize: function()
24300     {
24301         google.maps.event.trigger(this.gMapContext.map, "resize");
24302         
24303         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24304         
24305         this.fireEvent('resize', this);
24306     },
24307     
24308     setPositionByLatLng: function(latitude, longitude)
24309     {
24310         this.setPosition(new google.maps.LatLng(latitude, longitude));
24311     },
24312     
24313     getCurrentPosition: function() 
24314     {
24315         return {
24316             latitude: this.gMapContext.location.lat(),
24317             longitude: this.gMapContext.location.lng()
24318         };
24319     },
24320     
24321     getAddressName: function() 
24322     {
24323         return this.gMapContext.locationName;
24324     },
24325     
24326     getAddressComponents: function() 
24327     {
24328         return this.gMapContext.addressComponents;
24329     },
24330     
24331     address_component_from_google_geocode: function(address_components) 
24332     {
24333         var result = {};
24334         
24335         for (var i = 0; i < address_components.length; i++) {
24336             var component = address_components[i];
24337             if (component.types.indexOf("postal_code") >= 0) {
24338                 result.postalCode = component.short_name;
24339             } else if (component.types.indexOf("street_number") >= 0) {
24340                 result.streetNumber = component.short_name;
24341             } else if (component.types.indexOf("route") >= 0) {
24342                 result.streetName = component.short_name;
24343             } else if (component.types.indexOf("neighborhood") >= 0) {
24344                 result.city = component.short_name;
24345             } else if (component.types.indexOf("locality") >= 0) {
24346                 result.city = component.short_name;
24347             } else if (component.types.indexOf("sublocality") >= 0) {
24348                 result.district = component.short_name;
24349             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24350                 result.stateOrProvince = component.short_name;
24351             } else if (component.types.indexOf("country") >= 0) {
24352                 result.country = component.short_name;
24353             }
24354         }
24355         
24356         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24357         result.addressLine2 = "";
24358         return result;
24359     },
24360     
24361     setZoomLevel: function(zoom)
24362     {
24363         this.gMapContext.map.setZoom(zoom);
24364     },
24365     
24366     show: function()
24367     {
24368         if(!this.el){
24369             return;
24370         }
24371         
24372         this.el.show();
24373         
24374         this.resize();
24375         
24376         this.fireEvent('show', this);
24377     },
24378     
24379     hide: function()
24380     {
24381         if(!this.el){
24382             return;
24383         }
24384         
24385         this.el.hide();
24386         
24387         this.fireEvent('hide', this);
24388     }
24389     
24390 });
24391
24392 Roo.apply(Roo.bootstrap.LocationPicker, {
24393     
24394     OverlayView : function(map, options)
24395     {
24396         options = options || {};
24397         
24398         this.setMap(map);
24399     }
24400     
24401     
24402 });/*
24403  * - LGPL
24404  *
24405  * Alert
24406  * 
24407  */
24408
24409 /**
24410  * @class Roo.bootstrap.Alert
24411  * @extends Roo.bootstrap.Component
24412  * Bootstrap Alert class
24413  * @cfg {String} title The title of alert
24414  * @cfg {String} html The content of alert
24415  * @cfg {String} weight (  success | info | warning | danger )
24416  * @cfg {String} faicon font-awesomeicon
24417  * 
24418  * @constructor
24419  * Create a new alert
24420  * @param {Object} config The config object
24421  */
24422
24423
24424 Roo.bootstrap.Alert = function(config){
24425     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24426     
24427 };
24428
24429 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24430     
24431     title: '',
24432     html: '',
24433     weight: false,
24434     faicon: false,
24435     
24436     getAutoCreate : function()
24437     {
24438         
24439         var cfg = {
24440             tag : 'div',
24441             cls : 'alert',
24442             cn : [
24443                 {
24444                     tag : 'i',
24445                     cls : 'roo-alert-icon'
24446                     
24447                 },
24448                 {
24449                     tag : 'b',
24450                     cls : 'roo-alert-title',
24451                     html : this.title
24452                 },
24453                 {
24454                     tag : 'span',
24455                     cls : 'roo-alert-text',
24456                     html : this.html
24457                 }
24458             ]
24459         };
24460         
24461         if(this.faicon){
24462             cfg.cn[0].cls += ' fa ' + this.faicon;
24463         }
24464         
24465         if(this.weight){
24466             cfg.cls += ' alert-' + this.weight;
24467         }
24468         
24469         return cfg;
24470     },
24471     
24472     initEvents: function() 
24473     {
24474         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24475     },
24476     
24477     setTitle : function(str)
24478     {
24479         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24480     },
24481     
24482     setText : function(str)
24483     {
24484         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24485     },
24486     
24487     setWeight : function(weight)
24488     {
24489         if(this.weight){
24490             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24491         }
24492         
24493         this.weight = weight;
24494         
24495         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24496     },
24497     
24498     setIcon : function(icon)
24499     {
24500         if(this.faicon){
24501             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24502         }
24503         
24504         this.faicon = icon;
24505         
24506         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24507     },
24508     
24509     hide: function() 
24510     {
24511         this.el.hide();   
24512     },
24513     
24514     show: function() 
24515     {  
24516         this.el.show();   
24517     }
24518     
24519 });
24520
24521  
24522 /*
24523 * Licence: LGPL
24524 */
24525
24526 /**
24527  * @class Roo.bootstrap.UploadCropbox
24528  * @extends Roo.bootstrap.Component
24529  * Bootstrap UploadCropbox class
24530  * @cfg {String} emptyText show when image has been loaded
24531  * @cfg {String} rotateNotify show when image too small to rotate
24532  * @cfg {Number} errorTimeout default 3000
24533  * @cfg {Number} minWidth default 300
24534  * @cfg {Number} minHeight default 300
24535  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24536  * @cfg {Boolean} isDocument (true|false) default false
24537  * @cfg {String} url action url
24538  * @cfg {String} paramName default 'imageUpload'
24539  * @cfg {String} method default POST
24540  * @cfg {Boolean} loadMask (true|false) default true
24541  * @cfg {Boolean} loadingText default 'Loading...'
24542  * 
24543  * @constructor
24544  * Create a new UploadCropbox
24545  * @param {Object} config The config object
24546  */
24547
24548 Roo.bootstrap.UploadCropbox = function(config){
24549     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24550     
24551     this.addEvents({
24552         /**
24553          * @event beforeselectfile
24554          * Fire before select file
24555          * @param {Roo.bootstrap.UploadCropbox} this
24556          */
24557         "beforeselectfile" : true,
24558         /**
24559          * @event initial
24560          * Fire after initEvent
24561          * @param {Roo.bootstrap.UploadCropbox} this
24562          */
24563         "initial" : true,
24564         /**
24565          * @event crop
24566          * Fire after initEvent
24567          * @param {Roo.bootstrap.UploadCropbox} this
24568          * @param {String} data
24569          */
24570         "crop" : true,
24571         /**
24572          * @event prepare
24573          * Fire when preparing the file data
24574          * @param {Roo.bootstrap.UploadCropbox} this
24575          * @param {Object} file
24576          */
24577         "prepare" : true,
24578         /**
24579          * @event exception
24580          * Fire when get exception
24581          * @param {Roo.bootstrap.UploadCropbox} this
24582          * @param {XMLHttpRequest} xhr
24583          */
24584         "exception" : true,
24585         /**
24586          * @event beforeloadcanvas
24587          * Fire before load the canvas
24588          * @param {Roo.bootstrap.UploadCropbox} this
24589          * @param {String} src
24590          */
24591         "beforeloadcanvas" : true,
24592         /**
24593          * @event trash
24594          * Fire when trash image
24595          * @param {Roo.bootstrap.UploadCropbox} this
24596          */
24597         "trash" : true,
24598         /**
24599          * @event download
24600          * Fire when download the image
24601          * @param {Roo.bootstrap.UploadCropbox} this
24602          */
24603         "download" : true,
24604         /**
24605          * @event footerbuttonclick
24606          * Fire when footerbuttonclick
24607          * @param {Roo.bootstrap.UploadCropbox} this
24608          * @param {String} type
24609          */
24610         "footerbuttonclick" : true,
24611         /**
24612          * @event resize
24613          * Fire when resize
24614          * @param {Roo.bootstrap.UploadCropbox} this
24615          */
24616         "resize" : true,
24617         /**
24618          * @event rotate
24619          * Fire when rotate the image
24620          * @param {Roo.bootstrap.UploadCropbox} this
24621          * @param {String} pos
24622          */
24623         "rotate" : true,
24624         /**
24625          * @event inspect
24626          * Fire when inspect the file
24627          * @param {Roo.bootstrap.UploadCropbox} this
24628          * @param {Object} file
24629          */
24630         "inspect" : true,
24631         /**
24632          * @event upload
24633          * Fire when xhr upload the file
24634          * @param {Roo.bootstrap.UploadCropbox} this
24635          * @param {Object} data
24636          */
24637         "upload" : true,
24638         /**
24639          * @event arrange
24640          * Fire when arrange the file data
24641          * @param {Roo.bootstrap.UploadCropbox} this
24642          * @param {Object} formData
24643          */
24644         "arrange" : true
24645     });
24646     
24647     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24648 };
24649
24650 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24651     
24652     emptyText : 'Click to upload image',
24653     rotateNotify : 'Image is too small to rotate',
24654     errorTimeout : 3000,
24655     scale : 0,
24656     baseScale : 1,
24657     rotate : 0,
24658     dragable : false,
24659     pinching : false,
24660     mouseX : 0,
24661     mouseY : 0,
24662     cropData : false,
24663     minWidth : 300,
24664     minHeight : 300,
24665     file : false,
24666     exif : {},
24667     baseRotate : 1,
24668     cropType : 'image/jpeg',
24669     buttons : false,
24670     canvasLoaded : false,
24671     isDocument : false,
24672     method : 'POST',
24673     paramName : 'imageUpload',
24674     loadMask : true,
24675     loadingText : 'Loading...',
24676     maskEl : false,
24677     
24678     getAutoCreate : function()
24679     {
24680         var cfg = {
24681             tag : 'div',
24682             cls : 'roo-upload-cropbox',
24683             cn : [
24684                 {
24685                     tag : 'input',
24686                     cls : 'roo-upload-cropbox-selector',
24687                     type : 'file'
24688                 },
24689                 {
24690                     tag : 'div',
24691                     cls : 'roo-upload-cropbox-body',
24692                     style : 'cursor:pointer',
24693                     cn : [
24694                         {
24695                             tag : 'div',
24696                             cls : 'roo-upload-cropbox-preview'
24697                         },
24698                         {
24699                             tag : 'div',
24700                             cls : 'roo-upload-cropbox-thumb'
24701                         },
24702                         {
24703                             tag : 'div',
24704                             cls : 'roo-upload-cropbox-empty-notify',
24705                             html : this.emptyText
24706                         },
24707                         {
24708                             tag : 'div',
24709                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24710                             html : this.rotateNotify
24711                         }
24712                     ]
24713                 },
24714                 {
24715                     tag : 'div',
24716                     cls : 'roo-upload-cropbox-footer',
24717                     cn : {
24718                         tag : 'div',
24719                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24720                         cn : []
24721                     }
24722                 }
24723             ]
24724         };
24725         
24726         return cfg;
24727     },
24728     
24729     onRender : function(ct, position)
24730     {
24731         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24732         
24733         if (this.buttons.length) {
24734             
24735             Roo.each(this.buttons, function(bb) {
24736                 
24737                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24738                 
24739                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24740                 
24741             }, this);
24742         }
24743         
24744         if(this.loadMask){
24745             this.maskEl = this.el;
24746         }
24747     },
24748     
24749     initEvents : function()
24750     {
24751         this.urlAPI = (window.createObjectURL && window) || 
24752                                 (window.URL && URL.revokeObjectURL && URL) || 
24753                                 (window.webkitURL && webkitURL);
24754                         
24755         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24756         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24757         
24758         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24759         this.selectorEl.hide();
24760         
24761         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24762         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24763         
24764         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24765         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24766         this.thumbEl.hide();
24767         
24768         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24769         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24770         
24771         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24772         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24773         this.errorEl.hide();
24774         
24775         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24776         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24777         this.footerEl.hide();
24778         
24779         this.setThumbBoxSize();
24780         
24781         this.bind();
24782         
24783         this.resize();
24784         
24785         this.fireEvent('initial', this);
24786     },
24787
24788     bind : function()
24789     {
24790         var _this = this;
24791         
24792         window.addEventListener("resize", function() { _this.resize(); } );
24793         
24794         this.bodyEl.on('click', this.beforeSelectFile, this);
24795         
24796         if(Roo.isTouch){
24797             this.bodyEl.on('touchstart', this.onTouchStart, this);
24798             this.bodyEl.on('touchmove', this.onTouchMove, this);
24799             this.bodyEl.on('touchend', this.onTouchEnd, this);
24800         }
24801         
24802         if(!Roo.isTouch){
24803             this.bodyEl.on('mousedown', this.onMouseDown, this);
24804             this.bodyEl.on('mousemove', this.onMouseMove, this);
24805             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24806             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24807             Roo.get(document).on('mouseup', this.onMouseUp, this);
24808         }
24809         
24810         this.selectorEl.on('change', this.onFileSelected, this);
24811     },
24812     
24813     reset : function()
24814     {    
24815         this.scale = 0;
24816         this.baseScale = 1;
24817         this.rotate = 0;
24818         this.baseRotate = 1;
24819         this.dragable = false;
24820         this.pinching = false;
24821         this.mouseX = 0;
24822         this.mouseY = 0;
24823         this.cropData = false;
24824         this.notifyEl.dom.innerHTML = this.emptyText;
24825         
24826         this.selectorEl.dom.value = '';
24827         
24828     },
24829     
24830     resize : function()
24831     {
24832         if(this.fireEvent('resize', this) != false){
24833             this.setThumbBoxPosition();
24834             this.setCanvasPosition();
24835         }
24836     },
24837     
24838     onFooterButtonClick : function(e, el, o, type)
24839     {
24840         switch (type) {
24841             case 'rotate-left' :
24842                 this.onRotateLeft(e);
24843                 break;
24844             case 'rotate-right' :
24845                 this.onRotateRight(e);
24846                 break;
24847             case 'picture' :
24848                 this.beforeSelectFile(e);
24849                 break;
24850             case 'trash' :
24851                 this.trash(e);
24852                 break;
24853             case 'crop' :
24854                 this.crop(e);
24855                 break;
24856             case 'download' :
24857                 this.download(e);
24858                 break;
24859             default :
24860                 break;
24861         }
24862         
24863         this.fireEvent('footerbuttonclick', this, type);
24864     },
24865     
24866     beforeSelectFile : function(e)
24867     {
24868         e.preventDefault();
24869         
24870         if(this.fireEvent('beforeselectfile', this) != false){
24871             this.selectorEl.dom.click();
24872         }
24873     },
24874     
24875     onFileSelected : function(e)
24876     {
24877         e.preventDefault();
24878         
24879         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24880             return;
24881         }
24882         
24883         var file = this.selectorEl.dom.files[0];
24884         
24885         if(this.fireEvent('inspect', this, file) != false){
24886             this.prepare(file);
24887         }
24888         
24889     },
24890     
24891     trash : function(e)
24892     {
24893         this.fireEvent('trash', this);
24894     },
24895     
24896     download : function(e)
24897     {
24898         this.fireEvent('download', this);
24899     },
24900     
24901     loadCanvas : function(src)
24902     {   
24903         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24904             
24905             this.reset();
24906             
24907             this.imageEl = document.createElement('img');
24908             
24909             var _this = this;
24910             
24911             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24912             
24913             this.imageEl.src = src;
24914         }
24915     },
24916     
24917     onLoadCanvas : function()
24918     {   
24919         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24920         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
24921         
24922         this.bodyEl.un('click', this.beforeSelectFile, this);
24923         
24924         this.notifyEl.hide();
24925         this.thumbEl.show();
24926         this.footerEl.show();
24927         
24928         this.baseRotateLevel();
24929         
24930         if(this.isDocument){
24931             this.setThumbBoxSize();
24932         }
24933         
24934         this.setThumbBoxPosition();
24935         
24936         this.baseScaleLevel();
24937         
24938         this.draw();
24939         
24940         this.resize();
24941         
24942         this.canvasLoaded = true;
24943         
24944         if(this.loadMask){
24945             this.maskEl.unmask();
24946         }
24947         
24948     },
24949     
24950     setCanvasPosition : function()
24951     {   
24952         if(!this.canvasEl){
24953             return;
24954         }
24955         
24956         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
24957         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
24958         
24959         this.previewEl.setLeft(pw);
24960         this.previewEl.setTop(ph);
24961         
24962     },
24963     
24964     onMouseDown : function(e)
24965     {   
24966         e.stopEvent();
24967         
24968         this.dragable = true;
24969         this.pinching = false;
24970         
24971         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
24972             this.dragable = false;
24973             return;
24974         }
24975         
24976         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
24977         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
24978         
24979     },
24980     
24981     onMouseMove : function(e)
24982     {   
24983         e.stopEvent();
24984         
24985         if(!this.canvasLoaded){
24986             return;
24987         }
24988         
24989         if (!this.dragable){
24990             return;
24991         }
24992         
24993         var minX = Math.ceil(this.thumbEl.getLeft(true));
24994         var minY = Math.ceil(this.thumbEl.getTop(true));
24995         
24996         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
24997         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
24998         
24999         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25000         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25001         
25002         x = x - this.mouseX;
25003         y = y - this.mouseY;
25004         
25005         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25006         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25007         
25008         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25009         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25010         
25011         this.previewEl.setLeft(bgX);
25012         this.previewEl.setTop(bgY);
25013         
25014         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25015         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25016     },
25017     
25018     onMouseUp : function(e)
25019     {   
25020         e.stopEvent();
25021         
25022         this.dragable = false;
25023     },
25024     
25025     onMouseWheel : function(e)
25026     {   
25027         e.stopEvent();
25028         
25029         this.startScale = this.scale;
25030         
25031         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25032         
25033         if(!this.zoomable()){
25034             this.scale = this.startScale;
25035             return;
25036         }
25037         
25038         this.draw();
25039         
25040         return;
25041     },
25042     
25043     zoomable : function()
25044     {
25045         var minScale = this.thumbEl.getWidth() / this.minWidth;
25046         
25047         if(this.minWidth < this.minHeight){
25048             minScale = this.thumbEl.getHeight() / this.minHeight;
25049         }
25050         
25051         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25052         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25053         
25054         if(
25055                 this.isDocument &&
25056                 (this.rotate == 0 || this.rotate == 180) && 
25057                 (
25058                     width > this.imageEl.OriginWidth || 
25059                     height > this.imageEl.OriginHeight ||
25060                     (width < this.minWidth && height < this.minHeight)
25061                 )
25062         ){
25063             return false;
25064         }
25065         
25066         if(
25067                 this.isDocument &&
25068                 (this.rotate == 90 || this.rotate == 270) && 
25069                 (
25070                     width > this.imageEl.OriginWidth || 
25071                     height > this.imageEl.OriginHeight ||
25072                     (width < this.minHeight && height < this.minWidth)
25073                 )
25074         ){
25075             return false;
25076         }
25077         
25078         if(
25079                 !this.isDocument &&
25080                 (this.rotate == 0 || this.rotate == 180) && 
25081                 (
25082                     width < this.minWidth || 
25083                     width > this.imageEl.OriginWidth || 
25084                     height < this.minHeight || 
25085                     height > this.imageEl.OriginHeight
25086                 )
25087         ){
25088             return false;
25089         }
25090         
25091         if(
25092                 !this.isDocument &&
25093                 (this.rotate == 90 || this.rotate == 270) && 
25094                 (
25095                     width < this.minHeight || 
25096                     width > this.imageEl.OriginWidth || 
25097                     height < this.minWidth || 
25098                     height > this.imageEl.OriginHeight
25099                 )
25100         ){
25101             return false;
25102         }
25103         
25104         return true;
25105         
25106     },
25107     
25108     onRotateLeft : function(e)
25109     {   
25110         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25111             
25112             var minScale = this.thumbEl.getWidth() / this.minWidth;
25113             
25114             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25115             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25116             
25117             this.startScale = this.scale;
25118             
25119             while (this.getScaleLevel() < minScale){
25120             
25121                 this.scale = this.scale + 1;
25122                 
25123                 if(!this.zoomable()){
25124                     break;
25125                 }
25126                 
25127                 if(
25128                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25129                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25130                 ){
25131                     continue;
25132                 }
25133                 
25134                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25135
25136                 this.draw();
25137                 
25138                 return;
25139             }
25140             
25141             this.scale = this.startScale;
25142             
25143             this.onRotateFail();
25144             
25145             return false;
25146         }
25147         
25148         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25149
25150         if(this.isDocument){
25151             this.setThumbBoxSize();
25152             this.setThumbBoxPosition();
25153             this.setCanvasPosition();
25154         }
25155         
25156         this.draw();
25157         
25158         this.fireEvent('rotate', this, 'left');
25159         
25160     },
25161     
25162     onRotateRight : function(e)
25163     {
25164         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25165             
25166             var minScale = this.thumbEl.getWidth() / this.minWidth;
25167         
25168             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25169             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25170             
25171             this.startScale = this.scale;
25172             
25173             while (this.getScaleLevel() < minScale){
25174             
25175                 this.scale = this.scale + 1;
25176                 
25177                 if(!this.zoomable()){
25178                     break;
25179                 }
25180                 
25181                 if(
25182                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25183                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25184                 ){
25185                     continue;
25186                 }
25187                 
25188                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25189
25190                 this.draw();
25191                 
25192                 return;
25193             }
25194             
25195             this.scale = this.startScale;
25196             
25197             this.onRotateFail();
25198             
25199             return false;
25200         }
25201         
25202         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25203
25204         if(this.isDocument){
25205             this.setThumbBoxSize();
25206             this.setThumbBoxPosition();
25207             this.setCanvasPosition();
25208         }
25209         
25210         this.draw();
25211         
25212         this.fireEvent('rotate', this, 'right');
25213     },
25214     
25215     onRotateFail : function()
25216     {
25217         this.errorEl.show(true);
25218         
25219         var _this = this;
25220         
25221         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25222     },
25223     
25224     draw : function()
25225     {
25226         this.previewEl.dom.innerHTML = '';
25227         
25228         var canvasEl = document.createElement("canvas");
25229         
25230         var contextEl = canvasEl.getContext("2d");
25231         
25232         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25233         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25234         var center = this.imageEl.OriginWidth / 2;
25235         
25236         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25237             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25238             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25239             center = this.imageEl.OriginHeight / 2;
25240         }
25241         
25242         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25243         
25244         contextEl.translate(center, center);
25245         contextEl.rotate(this.rotate * Math.PI / 180);
25246
25247         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25248         
25249         this.canvasEl = document.createElement("canvas");
25250         
25251         this.contextEl = this.canvasEl.getContext("2d");
25252         
25253         switch (this.rotate) {
25254             case 0 :
25255                 
25256                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25257                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25258                 
25259                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25260                 
25261                 break;
25262             case 90 : 
25263                 
25264                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25265                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25266                 
25267                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25268                     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);
25269                     break;
25270                 }
25271                 
25272                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25273                 
25274                 break;
25275             case 180 :
25276                 
25277                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25278                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25279                 
25280                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25281                     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);
25282                     break;
25283                 }
25284                 
25285                 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);
25286                 
25287                 break;
25288             case 270 :
25289                 
25290                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25291                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25292         
25293                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25294                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25295                     break;
25296                 }
25297                 
25298                 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);
25299                 
25300                 break;
25301             default : 
25302                 break;
25303         }
25304         
25305         this.previewEl.appendChild(this.canvasEl);
25306         
25307         this.setCanvasPosition();
25308     },
25309     
25310     crop : function()
25311     {
25312         if(!this.canvasLoaded){
25313             return;
25314         }
25315         
25316         var imageCanvas = document.createElement("canvas");
25317         
25318         var imageContext = imageCanvas.getContext("2d");
25319         
25320         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25321         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25322         
25323         var center = imageCanvas.width / 2;
25324         
25325         imageContext.translate(center, center);
25326         
25327         imageContext.rotate(this.rotate * Math.PI / 180);
25328         
25329         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25330         
25331         var canvas = document.createElement("canvas");
25332         
25333         var context = canvas.getContext("2d");
25334                 
25335         canvas.width = this.minWidth;
25336         canvas.height = this.minHeight;
25337
25338         switch (this.rotate) {
25339             case 0 :
25340                 
25341                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25342                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25343                 
25344                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25345                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25346                 
25347                 var targetWidth = this.minWidth - 2 * x;
25348                 var targetHeight = this.minHeight - 2 * y;
25349                 
25350                 var scale = 1;
25351                 
25352                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25353                     scale = targetWidth / width;
25354                 }
25355                 
25356                 if(x > 0 && y == 0){
25357                     scale = targetHeight / height;
25358                 }
25359                 
25360                 if(x > 0 && y > 0){
25361                     scale = targetWidth / width;
25362                     
25363                     if(width < height){
25364                         scale = targetHeight / height;
25365                     }
25366                 }
25367                 
25368                 context.scale(scale, scale);
25369                 
25370                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25371                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25372
25373                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25374                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25375
25376                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25377                 
25378                 break;
25379             case 90 : 
25380                 
25381                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25382                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25383                 
25384                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25385                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25386                 
25387                 var targetWidth = this.minWidth - 2 * x;
25388                 var targetHeight = this.minHeight - 2 * y;
25389                 
25390                 var scale = 1;
25391                 
25392                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25393                     scale = targetWidth / width;
25394                 }
25395                 
25396                 if(x > 0 && y == 0){
25397                     scale = targetHeight / height;
25398                 }
25399                 
25400                 if(x > 0 && y > 0){
25401                     scale = targetWidth / width;
25402                     
25403                     if(width < height){
25404                         scale = targetHeight / height;
25405                     }
25406                 }
25407                 
25408                 context.scale(scale, scale);
25409                 
25410                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25411                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25412
25413                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25414                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25415                 
25416                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25417                 
25418                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25419                 
25420                 break;
25421             case 180 :
25422                 
25423                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25424                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25425                 
25426                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25427                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25428                 
25429                 var targetWidth = this.minWidth - 2 * x;
25430                 var targetHeight = this.minHeight - 2 * y;
25431                 
25432                 var scale = 1;
25433                 
25434                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25435                     scale = targetWidth / width;
25436                 }
25437                 
25438                 if(x > 0 && y == 0){
25439                     scale = targetHeight / height;
25440                 }
25441                 
25442                 if(x > 0 && y > 0){
25443                     scale = targetWidth / width;
25444                     
25445                     if(width < height){
25446                         scale = targetHeight / height;
25447                     }
25448                 }
25449                 
25450                 context.scale(scale, scale);
25451                 
25452                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25453                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25454
25455                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25456                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25457
25458                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25459                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25460                 
25461                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25462                 
25463                 break;
25464             case 270 :
25465                 
25466                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25467                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25468                 
25469                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25470                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25471                 
25472                 var targetWidth = this.minWidth - 2 * x;
25473                 var targetHeight = this.minHeight - 2 * y;
25474                 
25475                 var scale = 1;
25476                 
25477                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25478                     scale = targetWidth / width;
25479                 }
25480                 
25481                 if(x > 0 && y == 0){
25482                     scale = targetHeight / height;
25483                 }
25484                 
25485                 if(x > 0 && y > 0){
25486                     scale = targetWidth / width;
25487                     
25488                     if(width < height){
25489                         scale = targetHeight / height;
25490                     }
25491                 }
25492                 
25493                 context.scale(scale, scale);
25494                 
25495                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25496                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25497
25498                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25499                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25500                 
25501                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25502                 
25503                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25504                 
25505                 break;
25506             default : 
25507                 break;
25508         }
25509         
25510         this.cropData = canvas.toDataURL(this.cropType);
25511         
25512         if(this.fireEvent('crop', this, this.cropData) !== false){
25513             this.process(this.file, this.cropData);
25514         }
25515         
25516         return;
25517         
25518     },
25519     
25520     setThumbBoxSize : function()
25521     {
25522         var width, height;
25523         
25524         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25525             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25526             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25527             
25528             this.minWidth = width;
25529             this.minHeight = height;
25530             
25531             if(this.rotate == 90 || this.rotate == 270){
25532                 this.minWidth = height;
25533                 this.minHeight = width;
25534             }
25535         }
25536         
25537         height = 300;
25538         width = Math.ceil(this.minWidth * height / this.minHeight);
25539         
25540         if(this.minWidth > this.minHeight){
25541             width = 300;
25542             height = Math.ceil(this.minHeight * width / this.minWidth);
25543         }
25544         
25545         this.thumbEl.setStyle({
25546             width : width + 'px',
25547             height : height + 'px'
25548         });
25549
25550         return;
25551             
25552     },
25553     
25554     setThumbBoxPosition : function()
25555     {
25556         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25557         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25558         
25559         this.thumbEl.setLeft(x);
25560         this.thumbEl.setTop(y);
25561         
25562     },
25563     
25564     baseRotateLevel : function()
25565     {
25566         this.baseRotate = 1;
25567         
25568         if(
25569                 typeof(this.exif) != 'undefined' &&
25570                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25571                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25572         ){
25573             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25574         }
25575         
25576         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25577         
25578     },
25579     
25580     baseScaleLevel : function()
25581     {
25582         var width, height;
25583         
25584         if(this.isDocument){
25585             
25586             if(this.baseRotate == 6 || this.baseRotate == 8){
25587             
25588                 height = this.thumbEl.getHeight();
25589                 this.baseScale = height / this.imageEl.OriginWidth;
25590
25591                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25592                     width = this.thumbEl.getWidth();
25593                     this.baseScale = width / this.imageEl.OriginHeight;
25594                 }
25595
25596                 return;
25597             }
25598
25599             height = this.thumbEl.getHeight();
25600             this.baseScale = height / this.imageEl.OriginHeight;
25601
25602             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25603                 width = this.thumbEl.getWidth();
25604                 this.baseScale = width / this.imageEl.OriginWidth;
25605             }
25606
25607             return;
25608         }
25609         
25610         if(this.baseRotate == 6 || this.baseRotate == 8){
25611             
25612             width = this.thumbEl.getHeight();
25613             this.baseScale = width / this.imageEl.OriginHeight;
25614             
25615             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25616                 height = this.thumbEl.getWidth();
25617                 this.baseScale = height / this.imageEl.OriginHeight;
25618             }
25619             
25620             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25621                 height = this.thumbEl.getWidth();
25622                 this.baseScale = height / this.imageEl.OriginHeight;
25623                 
25624                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25625                     width = this.thumbEl.getHeight();
25626                     this.baseScale = width / this.imageEl.OriginWidth;
25627                 }
25628             }
25629             
25630             return;
25631         }
25632         
25633         width = this.thumbEl.getWidth();
25634         this.baseScale = width / this.imageEl.OriginWidth;
25635         
25636         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25637             height = this.thumbEl.getHeight();
25638             this.baseScale = height / this.imageEl.OriginHeight;
25639         }
25640         
25641         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25642             
25643             height = this.thumbEl.getHeight();
25644             this.baseScale = height / this.imageEl.OriginHeight;
25645             
25646             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25647                 width = this.thumbEl.getWidth();
25648                 this.baseScale = width / this.imageEl.OriginWidth;
25649             }
25650             
25651         }
25652         
25653         return;
25654     },
25655     
25656     getScaleLevel : function()
25657     {
25658         return this.baseScale * Math.pow(1.1, this.scale);
25659     },
25660     
25661     onTouchStart : function(e)
25662     {
25663         if(!this.canvasLoaded){
25664             this.beforeSelectFile(e);
25665             return;
25666         }
25667         
25668         var touches = e.browserEvent.touches;
25669         
25670         if(!touches){
25671             return;
25672         }
25673         
25674         if(touches.length == 1){
25675             this.onMouseDown(e);
25676             return;
25677         }
25678         
25679         if(touches.length != 2){
25680             return;
25681         }
25682         
25683         var coords = [];
25684         
25685         for(var i = 0, finger; finger = touches[i]; i++){
25686             coords.push(finger.pageX, finger.pageY);
25687         }
25688         
25689         var x = Math.pow(coords[0] - coords[2], 2);
25690         var y = Math.pow(coords[1] - coords[3], 2);
25691         
25692         this.startDistance = Math.sqrt(x + y);
25693         
25694         this.startScale = this.scale;
25695         
25696         this.pinching = true;
25697         this.dragable = false;
25698         
25699     },
25700     
25701     onTouchMove : function(e)
25702     {
25703         if(!this.pinching && !this.dragable){
25704             return;
25705         }
25706         
25707         var touches = e.browserEvent.touches;
25708         
25709         if(!touches){
25710             return;
25711         }
25712         
25713         if(this.dragable){
25714             this.onMouseMove(e);
25715             return;
25716         }
25717         
25718         var coords = [];
25719         
25720         for(var i = 0, finger; finger = touches[i]; i++){
25721             coords.push(finger.pageX, finger.pageY);
25722         }
25723         
25724         var x = Math.pow(coords[0] - coords[2], 2);
25725         var y = Math.pow(coords[1] - coords[3], 2);
25726         
25727         this.endDistance = Math.sqrt(x + y);
25728         
25729         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25730         
25731         if(!this.zoomable()){
25732             this.scale = this.startScale;
25733             return;
25734         }
25735         
25736         this.draw();
25737         
25738     },
25739     
25740     onTouchEnd : function(e)
25741     {
25742         this.pinching = false;
25743         this.dragable = false;
25744         
25745     },
25746     
25747     process : function(file, crop)
25748     {
25749         if(this.loadMask){
25750             this.maskEl.mask(this.loadingText);
25751         }
25752         
25753         this.xhr = new XMLHttpRequest();
25754         
25755         file.xhr = this.xhr;
25756
25757         this.xhr.open(this.method, this.url, true);
25758         
25759         var headers = {
25760             "Accept": "application/json",
25761             "Cache-Control": "no-cache",
25762             "X-Requested-With": "XMLHttpRequest"
25763         };
25764         
25765         for (var headerName in headers) {
25766             var headerValue = headers[headerName];
25767             if (headerValue) {
25768                 this.xhr.setRequestHeader(headerName, headerValue);
25769             }
25770         }
25771         
25772         var _this = this;
25773         
25774         this.xhr.onload = function()
25775         {
25776             _this.xhrOnLoad(_this.xhr);
25777         }
25778         
25779         this.xhr.onerror = function()
25780         {
25781             _this.xhrOnError(_this.xhr);
25782         }
25783         
25784         var formData = new FormData();
25785
25786         formData.append('returnHTML', 'NO');
25787         
25788         if(crop){
25789             formData.append('crop', crop);
25790         }
25791         
25792         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25793             formData.append(this.paramName, file, file.name);
25794         }
25795         
25796         if(typeof(file.filename) != 'undefined'){
25797             formData.append('filename', file.filename);
25798         }
25799         
25800         if(typeof(file.mimetype) != 'undefined'){
25801             formData.append('mimetype', file.mimetype);
25802         }
25803         
25804         if(this.fireEvent('arrange', this, formData) != false){
25805             this.xhr.send(formData);
25806         };
25807     },
25808     
25809     xhrOnLoad : function(xhr)
25810     {
25811         if(this.loadMask){
25812             this.maskEl.unmask();
25813         }
25814         
25815         if (xhr.readyState !== 4) {
25816             this.fireEvent('exception', this, xhr);
25817             return;
25818         }
25819
25820         var response = Roo.decode(xhr.responseText);
25821         
25822         if(!response.success){
25823             this.fireEvent('exception', this, xhr);
25824             return;
25825         }
25826         
25827         var response = Roo.decode(xhr.responseText);
25828         
25829         this.fireEvent('upload', this, response);
25830         
25831     },
25832     
25833     xhrOnError : function()
25834     {
25835         if(this.loadMask){
25836             this.maskEl.unmask();
25837         }
25838         
25839         Roo.log('xhr on error');
25840         
25841         var response = Roo.decode(xhr.responseText);
25842           
25843         Roo.log(response);
25844         
25845     },
25846     
25847     prepare : function(file)
25848     {   
25849         if(this.loadMask){
25850             this.maskEl.mask(this.loadingText);
25851         }
25852         
25853         this.file = false;
25854         this.exif = {};
25855         
25856         if(typeof(file) === 'string'){
25857             this.loadCanvas(file);
25858             return;
25859         }
25860         
25861         if(!file || !this.urlAPI){
25862             return;
25863         }
25864         
25865         this.file = file;
25866         this.cropType = file.type;
25867         
25868         var _this = this;
25869         
25870         if(this.fireEvent('prepare', this, this.file) != false){
25871             
25872             var reader = new FileReader();
25873             
25874             reader.onload = function (e) {
25875                 if (e.target.error) {
25876                     Roo.log(e.target.error);
25877                     return;
25878                 }
25879                 
25880                 var buffer = e.target.result,
25881                     dataView = new DataView(buffer),
25882                     offset = 2,
25883                     maxOffset = dataView.byteLength - 4,
25884                     markerBytes,
25885                     markerLength;
25886                 
25887                 if (dataView.getUint16(0) === 0xffd8) {
25888                     while (offset < maxOffset) {
25889                         markerBytes = dataView.getUint16(offset);
25890                         
25891                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25892                             markerLength = dataView.getUint16(offset + 2) + 2;
25893                             if (offset + markerLength > dataView.byteLength) {
25894                                 Roo.log('Invalid meta data: Invalid segment size.');
25895                                 break;
25896                             }
25897                             
25898                             if(markerBytes == 0xffe1){
25899                                 _this.parseExifData(
25900                                     dataView,
25901                                     offset,
25902                                     markerLength
25903                                 );
25904                             }
25905                             
25906                             offset += markerLength;
25907                             
25908                             continue;
25909                         }
25910                         
25911                         break;
25912                     }
25913                     
25914                 }
25915                 
25916                 var url = _this.urlAPI.createObjectURL(_this.file);
25917                 
25918                 _this.loadCanvas(url);
25919                 
25920                 return;
25921             }
25922             
25923             reader.readAsArrayBuffer(this.file);
25924             
25925         }
25926         
25927     },
25928     
25929     parseExifData : function(dataView, offset, length)
25930     {
25931         var tiffOffset = offset + 10,
25932             littleEndian,
25933             dirOffset;
25934     
25935         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25936             // No Exif data, might be XMP data instead
25937             return;
25938         }
25939         
25940         // Check for the ASCII code for "Exif" (0x45786966):
25941         if (dataView.getUint32(offset + 4) !== 0x45786966) {
25942             // No Exif data, might be XMP data instead
25943             return;
25944         }
25945         if (tiffOffset + 8 > dataView.byteLength) {
25946             Roo.log('Invalid Exif data: Invalid segment size.');
25947             return;
25948         }
25949         // Check for the two null bytes:
25950         if (dataView.getUint16(offset + 8) !== 0x0000) {
25951             Roo.log('Invalid Exif data: Missing byte alignment offset.');
25952             return;
25953         }
25954         // Check the byte alignment:
25955         switch (dataView.getUint16(tiffOffset)) {
25956         case 0x4949:
25957             littleEndian = true;
25958             break;
25959         case 0x4D4D:
25960             littleEndian = false;
25961             break;
25962         default:
25963             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
25964             return;
25965         }
25966         // Check for the TIFF tag marker (0x002A):
25967         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
25968             Roo.log('Invalid Exif data: Missing TIFF marker.');
25969             return;
25970         }
25971         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
25972         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
25973         
25974         this.parseExifTags(
25975             dataView,
25976             tiffOffset,
25977             tiffOffset + dirOffset,
25978             littleEndian
25979         );
25980     },
25981     
25982     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
25983     {
25984         var tagsNumber,
25985             dirEndOffset,
25986             i;
25987         if (dirOffset + 6 > dataView.byteLength) {
25988             Roo.log('Invalid Exif data: Invalid directory offset.');
25989             return;
25990         }
25991         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
25992         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
25993         if (dirEndOffset + 4 > dataView.byteLength) {
25994             Roo.log('Invalid Exif data: Invalid directory size.');
25995             return;
25996         }
25997         for (i = 0; i < tagsNumber; i += 1) {
25998             this.parseExifTag(
25999                 dataView,
26000                 tiffOffset,
26001                 dirOffset + 2 + 12 * i, // tag offset
26002                 littleEndian
26003             );
26004         }
26005         // Return the offset to the next directory:
26006         return dataView.getUint32(dirEndOffset, littleEndian);
26007     },
26008     
26009     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26010     {
26011         var tag = dataView.getUint16(offset, littleEndian);
26012         
26013         this.exif[tag] = this.getExifValue(
26014             dataView,
26015             tiffOffset,
26016             offset,
26017             dataView.getUint16(offset + 2, littleEndian), // tag type
26018             dataView.getUint32(offset + 4, littleEndian), // tag length
26019             littleEndian
26020         );
26021     },
26022     
26023     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26024     {
26025         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26026             tagSize,
26027             dataOffset,
26028             values,
26029             i,
26030             str,
26031             c;
26032     
26033         if (!tagType) {
26034             Roo.log('Invalid Exif data: Invalid tag type.');
26035             return;
26036         }
26037         
26038         tagSize = tagType.size * length;
26039         // Determine if the value is contained in the dataOffset bytes,
26040         // or if the value at the dataOffset is a pointer to the actual data:
26041         dataOffset = tagSize > 4 ?
26042                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26043         if (dataOffset + tagSize > dataView.byteLength) {
26044             Roo.log('Invalid Exif data: Invalid data offset.');
26045             return;
26046         }
26047         if (length === 1) {
26048             return tagType.getValue(dataView, dataOffset, littleEndian);
26049         }
26050         values = [];
26051         for (i = 0; i < length; i += 1) {
26052             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26053         }
26054         
26055         if (tagType.ascii) {
26056             str = '';
26057             // Concatenate the chars:
26058             for (i = 0; i < values.length; i += 1) {
26059                 c = values[i];
26060                 // Ignore the terminating NULL byte(s):
26061                 if (c === '\u0000') {
26062                     break;
26063                 }
26064                 str += c;
26065             }
26066             return str;
26067         }
26068         return values;
26069     }
26070     
26071 });
26072
26073 Roo.apply(Roo.bootstrap.UploadCropbox, {
26074     tags : {
26075         'Orientation': 0x0112
26076     },
26077     
26078     Orientation: {
26079             1: 0, //'top-left',
26080 //            2: 'top-right',
26081             3: 180, //'bottom-right',
26082 //            4: 'bottom-left',
26083 //            5: 'left-top',
26084             6: 90, //'right-top',
26085 //            7: 'right-bottom',
26086             8: 270 //'left-bottom'
26087     },
26088     
26089     exifTagTypes : {
26090         // byte, 8-bit unsigned int:
26091         1: {
26092             getValue: function (dataView, dataOffset) {
26093                 return dataView.getUint8(dataOffset);
26094             },
26095             size: 1
26096         },
26097         // ascii, 8-bit byte:
26098         2: {
26099             getValue: function (dataView, dataOffset) {
26100                 return String.fromCharCode(dataView.getUint8(dataOffset));
26101             },
26102             size: 1,
26103             ascii: true
26104         },
26105         // short, 16 bit int:
26106         3: {
26107             getValue: function (dataView, dataOffset, littleEndian) {
26108                 return dataView.getUint16(dataOffset, littleEndian);
26109             },
26110             size: 2
26111         },
26112         // long, 32 bit int:
26113         4: {
26114             getValue: function (dataView, dataOffset, littleEndian) {
26115                 return dataView.getUint32(dataOffset, littleEndian);
26116             },
26117             size: 4
26118         },
26119         // rational = two long values, first is numerator, second is denominator:
26120         5: {
26121             getValue: function (dataView, dataOffset, littleEndian) {
26122                 return dataView.getUint32(dataOffset, littleEndian) /
26123                     dataView.getUint32(dataOffset + 4, littleEndian);
26124             },
26125             size: 8
26126         },
26127         // slong, 32 bit signed int:
26128         9: {
26129             getValue: function (dataView, dataOffset, littleEndian) {
26130                 return dataView.getInt32(dataOffset, littleEndian);
26131             },
26132             size: 4
26133         },
26134         // srational, two slongs, first is numerator, second is denominator:
26135         10: {
26136             getValue: function (dataView, dataOffset, littleEndian) {
26137                 return dataView.getInt32(dataOffset, littleEndian) /
26138                     dataView.getInt32(dataOffset + 4, littleEndian);
26139             },
26140             size: 8
26141         }
26142     },
26143     
26144     footer : {
26145         STANDARD : [
26146             {
26147                 tag : 'div',
26148                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26149                 action : 'rotate-left',
26150                 cn : [
26151                     {
26152                         tag : 'button',
26153                         cls : 'btn btn-default',
26154                         html : '<i class="fa fa-undo"></i>'
26155                     }
26156                 ]
26157             },
26158             {
26159                 tag : 'div',
26160                 cls : 'btn-group roo-upload-cropbox-picture',
26161                 action : 'picture',
26162                 cn : [
26163                     {
26164                         tag : 'button',
26165                         cls : 'btn btn-default',
26166                         html : '<i class="fa fa-picture-o"></i>'
26167                     }
26168                 ]
26169             },
26170             {
26171                 tag : 'div',
26172                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26173                 action : 'rotate-right',
26174                 cn : [
26175                     {
26176                         tag : 'button',
26177                         cls : 'btn btn-default',
26178                         html : '<i class="fa fa-repeat"></i>'
26179                     }
26180                 ]
26181             }
26182         ],
26183         DOCUMENT : [
26184             {
26185                 tag : 'div',
26186                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26187                 action : 'rotate-left',
26188                 cn : [
26189                     {
26190                         tag : 'button',
26191                         cls : 'btn btn-default',
26192                         html : '<i class="fa fa-undo"></i>'
26193                     }
26194                 ]
26195             },
26196             {
26197                 tag : 'div',
26198                 cls : 'btn-group roo-upload-cropbox-download',
26199                 action : 'download',
26200                 cn : [
26201                     {
26202                         tag : 'button',
26203                         cls : 'btn btn-default',
26204                         html : '<i class="fa fa-download"></i>'
26205                     }
26206                 ]
26207             },
26208             {
26209                 tag : 'div',
26210                 cls : 'btn-group roo-upload-cropbox-crop',
26211                 action : 'crop',
26212                 cn : [
26213                     {
26214                         tag : 'button',
26215                         cls : 'btn btn-default',
26216                         html : '<i class="fa fa-crop"></i>'
26217                     }
26218                 ]
26219             },
26220             {
26221                 tag : 'div',
26222                 cls : 'btn-group roo-upload-cropbox-trash',
26223                 action : 'trash',
26224                 cn : [
26225                     {
26226                         tag : 'button',
26227                         cls : 'btn btn-default',
26228                         html : '<i class="fa fa-trash"></i>'
26229                     }
26230                 ]
26231             },
26232             {
26233                 tag : 'div',
26234                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26235                 action : 'rotate-right',
26236                 cn : [
26237                     {
26238                         tag : 'button',
26239                         cls : 'btn btn-default',
26240                         html : '<i class="fa fa-repeat"></i>'
26241                     }
26242                 ]
26243             }
26244         ],
26245         ROTATOR : [
26246             {
26247                 tag : 'div',
26248                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26249                 action : 'rotate-left',
26250                 cn : [
26251                     {
26252                         tag : 'button',
26253                         cls : 'btn btn-default',
26254                         html : '<i class="fa fa-undo"></i>'
26255                     }
26256                 ]
26257             },
26258             {
26259                 tag : 'div',
26260                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26261                 action : 'rotate-right',
26262                 cn : [
26263                     {
26264                         tag : 'button',
26265                         cls : 'btn btn-default',
26266                         html : '<i class="fa fa-repeat"></i>'
26267                     }
26268                 ]
26269             }
26270         ]
26271     }
26272 });
26273
26274 /*
26275 * Licence: LGPL
26276 */
26277
26278 /**
26279  * @class Roo.bootstrap.DocumentManager
26280  * @extends Roo.bootstrap.Component
26281  * Bootstrap DocumentManager class
26282  * @cfg {String} paramName default 'imageUpload'
26283  * @cfg {String} method default POST
26284  * @cfg {String} url action url
26285  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26286  * @cfg {Boolean} multiple multiple upload default true
26287  * @cfg {Number} thumbSize default 300
26288  * @cfg {String} fieldLabel
26289  * @cfg {Number} labelWidth default 4
26290  * @cfg {String} labelAlign (left|top) default left
26291  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26292  * 
26293  * @constructor
26294  * Create a new DocumentManager
26295  * @param {Object} config The config object
26296  */
26297
26298 Roo.bootstrap.DocumentManager = function(config){
26299     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26300     
26301     this.addEvents({
26302         /**
26303          * @event initial
26304          * Fire when initial the DocumentManager
26305          * @param {Roo.bootstrap.DocumentManager} this
26306          */
26307         "initial" : true,
26308         /**
26309          * @event inspect
26310          * inspect selected file
26311          * @param {Roo.bootstrap.DocumentManager} this
26312          * @param {File} file
26313          */
26314         "inspect" : true,
26315         /**
26316          * @event exception
26317          * Fire when xhr load exception
26318          * @param {Roo.bootstrap.DocumentManager} this
26319          * @param {XMLHttpRequest} xhr
26320          */
26321         "exception" : true,
26322         /**
26323          * @event prepare
26324          * prepare the form data
26325          * @param {Roo.bootstrap.DocumentManager} this
26326          * @param {Object} formData
26327          */
26328         "prepare" : true,
26329         /**
26330          * @event remove
26331          * Fire when remove the file
26332          * @param {Roo.bootstrap.DocumentManager} this
26333          * @param {Object} file
26334          */
26335         "remove" : true,
26336         /**
26337          * @event refresh
26338          * Fire after refresh the file
26339          * @param {Roo.bootstrap.DocumentManager} this
26340          */
26341         "refresh" : true,
26342         /**
26343          * @event click
26344          * Fire after click the image
26345          * @param {Roo.bootstrap.DocumentManager} this
26346          * @param {Object} file
26347          */
26348         "click" : true,
26349         /**
26350          * @event edit
26351          * Fire when upload a image and editable set to true
26352          * @param {Roo.bootstrap.DocumentManager} this
26353          * @param {Object} file
26354          */
26355         "edit" : true,
26356         /**
26357          * @event beforeselectfile
26358          * Fire before select file
26359          * @param {Roo.bootstrap.DocumentManager} this
26360          */
26361         "beforeselectfile" : true,
26362         /**
26363          * @event process
26364          * Fire before process file
26365          * @param {Roo.bootstrap.DocumentManager} this
26366          * @param {Object} file
26367          */
26368         "process" : true
26369         
26370     });
26371 };
26372
26373 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26374     
26375     boxes : 0,
26376     inputName : '',
26377     thumbSize : 300,
26378     multiple : true,
26379     files : [],
26380     method : 'POST',
26381     url : '',
26382     paramName : 'imageUpload',
26383     fieldLabel : '',
26384     labelWidth : 4,
26385     labelAlign : 'left',
26386     editable : true,
26387     delegates : [],
26388     
26389     
26390     xhr : false, 
26391     
26392     getAutoCreate : function()
26393     {   
26394         var managerWidget = {
26395             tag : 'div',
26396             cls : 'roo-document-manager',
26397             cn : [
26398                 {
26399                     tag : 'input',
26400                     cls : 'roo-document-manager-selector',
26401                     type : 'file'
26402                 },
26403                 {
26404                     tag : 'div',
26405                     cls : 'roo-document-manager-uploader',
26406                     cn : [
26407                         {
26408                             tag : 'div',
26409                             cls : 'roo-document-manager-upload-btn',
26410                             html : '<i class="fa fa-plus"></i>'
26411                         }
26412                     ]
26413                     
26414                 }
26415             ]
26416         };
26417         
26418         var content = [
26419             {
26420                 tag : 'div',
26421                 cls : 'column col-md-12',
26422                 cn : managerWidget
26423             }
26424         ];
26425         
26426         if(this.fieldLabel.length){
26427             
26428             content = [
26429                 {
26430                     tag : 'div',
26431                     cls : 'column col-md-12',
26432                     html : this.fieldLabel
26433                 },
26434                 {
26435                     tag : 'div',
26436                     cls : 'column col-md-12',
26437                     cn : managerWidget
26438                 }
26439             ];
26440
26441             if(this.labelAlign == 'left'){
26442                 content = [
26443                     {
26444                         tag : 'div',
26445                         cls : 'column col-md-' + this.labelWidth,
26446                         html : this.fieldLabel
26447                     },
26448                     {
26449                         tag : 'div',
26450                         cls : 'column col-md-' + (12 - this.labelWidth),
26451                         cn : managerWidget
26452                     }
26453                 ];
26454                 
26455             }
26456         }
26457         
26458         var cfg = {
26459             tag : 'div',
26460             cls : 'row clearfix',
26461             cn : content
26462         };
26463         
26464         return cfg;
26465         
26466     },
26467     
26468     initEvents : function()
26469     {
26470         this.managerEl = this.el.select('.roo-document-manager', true).first();
26471         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26472         
26473         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26474         this.selectorEl.hide();
26475         
26476         if(this.multiple){
26477             this.selectorEl.attr('multiple', 'multiple');
26478         }
26479         
26480         this.selectorEl.on('change', this.onFileSelected, this);
26481         
26482         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26483         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26484         
26485         this.uploader.on('click', this.onUploaderClick, this);
26486         
26487         this.renderProgressDialog();
26488         
26489         var _this = this;
26490         
26491         window.addEventListener("resize", function() { _this.refresh(); } );
26492         
26493         this.fireEvent('initial', this);
26494     },
26495     
26496     renderProgressDialog : function()
26497     {
26498         var _this = this;
26499         
26500         this.progressDialog = new Roo.bootstrap.Modal({
26501             cls : 'roo-document-manager-progress-dialog',
26502             allow_close : false,
26503             title : '',
26504             buttons : [
26505                 {
26506                     name  :'cancel',
26507                     weight : 'danger',
26508                     html : 'Cancel'
26509                 }
26510             ], 
26511             listeners : { 
26512                 btnclick : function() {
26513                     _this.uploadCancel();
26514                     this.hide();
26515                 }
26516             }
26517         });
26518          
26519         this.progressDialog.render(Roo.get(document.body));
26520          
26521         this.progress = new Roo.bootstrap.Progress({
26522             cls : 'roo-document-manager-progress',
26523             active : true,
26524             striped : true
26525         });
26526         
26527         this.progress.render(this.progressDialog.getChildContainer());
26528         
26529         this.progressBar = new Roo.bootstrap.ProgressBar({
26530             cls : 'roo-document-manager-progress-bar',
26531             aria_valuenow : 0,
26532             aria_valuemin : 0,
26533             aria_valuemax : 12,
26534             panel : 'success'
26535         });
26536         
26537         this.progressBar.render(this.progress.getChildContainer());
26538     },
26539     
26540     onUploaderClick : function(e)
26541     {
26542         e.preventDefault();
26543      
26544         if(this.fireEvent('beforeselectfile', this) != false){
26545             this.selectorEl.dom.click();
26546         }
26547         
26548     },
26549     
26550     onFileSelected : function(e)
26551     {
26552         e.preventDefault();
26553         
26554         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26555             return;
26556         }
26557         
26558         Roo.each(this.selectorEl.dom.files, function(file){
26559             if(this.fireEvent('inspect', this, file) != false){
26560                 this.files.push(file);
26561             }
26562         }, this);
26563         
26564         this.queue();
26565         
26566     },
26567     
26568     queue : function()
26569     {
26570         this.selectorEl.dom.value = '';
26571         
26572         if(!this.files.length){
26573             return;
26574         }
26575         
26576         if(this.boxes > 0 && this.files.length > this.boxes){
26577             this.files = this.files.slice(0, this.boxes);
26578         }
26579         
26580         this.uploader.show();
26581         
26582         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26583             this.uploader.hide();
26584         }
26585         
26586         var _this = this;
26587         
26588         var files = [];
26589         
26590         var docs = [];
26591         
26592         Roo.each(this.files, function(file){
26593             
26594             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26595                 var f = this.renderPreview(file);
26596                 files.push(f);
26597                 return;
26598             }
26599             
26600             if(file.type.indexOf('image') != -1){
26601                 this.delegates.push(
26602                     (function(){
26603                         _this.process(file);
26604                     }).createDelegate(this)
26605                 );
26606         
26607                 return;
26608             }
26609             
26610             docs.push(
26611                 (function(){
26612                     _this.process(file);
26613                 }).createDelegate(this)
26614             );
26615             
26616         }, this);
26617         
26618         this.files = files;
26619         
26620         this.delegates = this.delegates.concat(docs);
26621         
26622         if(!this.delegates.length){
26623             this.refresh();
26624             return;
26625         }
26626         
26627         this.progressBar.aria_valuemax = this.delegates.length;
26628         
26629         this.arrange();
26630         
26631         return;
26632     },
26633     
26634     arrange : function()
26635     {
26636         if(!this.delegates.length){
26637             this.progressDialog.hide();
26638             this.refresh();
26639             return;
26640         }
26641         
26642         var delegate = this.delegates.shift();
26643         
26644         this.progressDialog.show();
26645         
26646         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26647         
26648         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26649         
26650         delegate();
26651     },
26652     
26653     refresh : function()
26654     {
26655         this.uploader.show();
26656         
26657         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26658             this.uploader.hide();
26659         }
26660         
26661         Roo.isTouch ? this.closable(false) : this.closable(true);
26662         
26663         this.fireEvent('refresh', this);
26664     },
26665     
26666     onRemove : function(e, el, o)
26667     {
26668         e.preventDefault();
26669         
26670         this.fireEvent('remove', this, o);
26671         
26672     },
26673     
26674     remove : function(o)
26675     {
26676         var files = [];
26677         
26678         Roo.each(this.files, function(file){
26679             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26680                 files.push(file);
26681                 return;
26682             }
26683
26684             o.target.remove();
26685
26686         }, this);
26687         
26688         this.files = files;
26689         
26690         this.refresh();
26691     },
26692     
26693     clear : function()
26694     {
26695         Roo.each(this.files, function(file){
26696             if(!file.target){
26697                 return;
26698             }
26699             
26700             file.target.remove();
26701
26702         }, this);
26703         
26704         this.files = [];
26705         
26706         this.refresh();
26707     },
26708     
26709     onClick : function(e, el, o)
26710     {
26711         e.preventDefault();
26712         
26713         this.fireEvent('click', this, o);
26714         
26715     },
26716     
26717     closable : function(closable)
26718     {
26719         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26720             
26721             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26722             
26723             if(closable){
26724                 el.show();
26725                 return;
26726             }
26727             
26728             el.hide();
26729             
26730         }, this);
26731     },
26732     
26733     xhrOnLoad : function(xhr)
26734     {
26735         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26736             el.remove();
26737         }, this);
26738         
26739         if (xhr.readyState !== 4) {
26740             this.arrange();
26741             this.fireEvent('exception', this, xhr);
26742             return;
26743         }
26744
26745         var response = Roo.decode(xhr.responseText);
26746         
26747         if(!response.success){
26748             this.arrange();
26749             this.fireEvent('exception', this, xhr);
26750             return;
26751         }
26752         
26753         var file = this.renderPreview(response.data);
26754         
26755         this.files.push(file);
26756         
26757         this.arrange();
26758         
26759     },
26760     
26761     xhrOnError : function(xhr)
26762     {
26763         Roo.log('xhr on error');
26764         
26765         var response = Roo.decode(xhr.responseText);
26766           
26767         Roo.log(response);
26768         
26769         this.arrange();
26770     },
26771     
26772     process : function(file)
26773     {
26774         if(this.fireEvent('process', this, file) !== false){
26775             if(this.editable && file.type.indexOf('image') != -1){
26776                 this.fireEvent('edit', this, file);
26777                 return;
26778             }
26779
26780             this.uploadStart(file, false);
26781
26782             return;
26783         }
26784         
26785     },
26786     
26787     uploadStart : function(file, crop)
26788     {
26789         this.xhr = new XMLHttpRequest();
26790         
26791         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26792             this.arrange();
26793             return;
26794         }
26795         
26796         file.xhr = this.xhr;
26797             
26798         this.managerEl.createChild({
26799             tag : 'div',
26800             cls : 'roo-document-manager-loading',
26801             cn : [
26802                 {
26803                     tag : 'div',
26804                     tooltip : file.name,
26805                     cls : 'roo-document-manager-thumb',
26806                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26807                 }
26808             ]
26809
26810         });
26811
26812         this.xhr.open(this.method, this.url, true);
26813         
26814         var headers = {
26815             "Accept": "application/json",
26816             "Cache-Control": "no-cache",
26817             "X-Requested-With": "XMLHttpRequest"
26818         };
26819         
26820         for (var headerName in headers) {
26821             var headerValue = headers[headerName];
26822             if (headerValue) {
26823                 this.xhr.setRequestHeader(headerName, headerValue);
26824             }
26825         }
26826         
26827         var _this = this;
26828         
26829         this.xhr.onload = function()
26830         {
26831             _this.xhrOnLoad(_this.xhr);
26832         }
26833         
26834         this.xhr.onerror = function()
26835         {
26836             _this.xhrOnError(_this.xhr);
26837         }
26838         
26839         var formData = new FormData();
26840
26841         formData.append('returnHTML', 'NO');
26842         
26843         if(crop){
26844             formData.append('crop', crop);
26845         }
26846         
26847         formData.append(this.paramName, file, file.name);
26848         
26849         if(this.fireEvent('prepare', this, formData) != false){
26850             this.xhr.send(formData);
26851         };
26852     },
26853     
26854     uploadCancel : function()
26855     {
26856         if (this.xhr) {
26857             this.xhr.abort();
26858         }
26859         
26860         
26861         this.delegates = [];
26862         
26863         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26864             el.remove();
26865         }, this);
26866         
26867         this.arrange();
26868     },
26869     
26870     renderPreview : function(file)
26871     {
26872         if(typeof(file.target) != 'undefined' && file.target){
26873             return file;
26874         }
26875         
26876         var previewEl = this.managerEl.createChild({
26877             tag : 'div',
26878             cls : 'roo-document-manager-preview',
26879             cn : [
26880                 {
26881                     tag : 'div',
26882                     tooltip : file.filename,
26883                     cls : 'roo-document-manager-thumb',
26884                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26885                 },
26886                 {
26887                     tag : 'button',
26888                     cls : 'close',
26889                     html : '<i class="fa fa-times-circle"></i>'
26890                 }
26891             ]
26892         });
26893
26894         var close = previewEl.select('button.close', true).first();
26895
26896         close.on('click', this.onRemove, this, file);
26897
26898         file.target = previewEl;
26899
26900         var image = previewEl.select('img', true).first();
26901         
26902         var _this = this;
26903         
26904         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26905         
26906         image.on('click', this.onClick, this, file);
26907         
26908         return file;
26909         
26910     },
26911     
26912     onPreviewLoad : function(file, image)
26913     {
26914         if(typeof(file.target) == 'undefined' || !file.target){
26915             return;
26916         }
26917         
26918         var width = image.dom.naturalWidth || image.dom.width;
26919         var height = image.dom.naturalHeight || image.dom.height;
26920         
26921         if(width > height){
26922             file.target.addClass('wide');
26923             return;
26924         }
26925         
26926         file.target.addClass('tall');
26927         return;
26928         
26929     },
26930     
26931     uploadFromSource : function(file, crop)
26932     {
26933         this.xhr = new XMLHttpRequest();
26934         
26935         this.managerEl.createChild({
26936             tag : 'div',
26937             cls : 'roo-document-manager-loading',
26938             cn : [
26939                 {
26940                     tag : 'div',
26941                     tooltip : file.name,
26942                     cls : 'roo-document-manager-thumb',
26943                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26944                 }
26945             ]
26946
26947         });
26948
26949         this.xhr.open(this.method, this.url, true);
26950         
26951         var headers = {
26952             "Accept": "application/json",
26953             "Cache-Control": "no-cache",
26954             "X-Requested-With": "XMLHttpRequest"
26955         };
26956         
26957         for (var headerName in headers) {
26958             var headerValue = headers[headerName];
26959             if (headerValue) {
26960                 this.xhr.setRequestHeader(headerName, headerValue);
26961             }
26962         }
26963         
26964         var _this = this;
26965         
26966         this.xhr.onload = function()
26967         {
26968             _this.xhrOnLoad(_this.xhr);
26969         }
26970         
26971         this.xhr.onerror = function()
26972         {
26973             _this.xhrOnError(_this.xhr);
26974         }
26975         
26976         var formData = new FormData();
26977
26978         formData.append('returnHTML', 'NO');
26979         
26980         formData.append('crop', crop);
26981         
26982         if(typeof(file.filename) != 'undefined'){
26983             formData.append('filename', file.filename);
26984         }
26985         
26986         if(typeof(file.mimetype) != 'undefined'){
26987             formData.append('mimetype', file.mimetype);
26988         }
26989         
26990         if(this.fireEvent('prepare', this, formData) != false){
26991             this.xhr.send(formData);
26992         };
26993     }
26994 });
26995
26996 /*
26997 * Licence: LGPL
26998 */
26999
27000 /**
27001  * @class Roo.bootstrap.DocumentViewer
27002  * @extends Roo.bootstrap.Component
27003  * Bootstrap DocumentViewer class
27004  * 
27005  * @constructor
27006  * Create a new DocumentViewer
27007  * @param {Object} config The config object
27008  */
27009
27010 Roo.bootstrap.DocumentViewer = function(config){
27011     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27012     
27013     this.addEvents({
27014         /**
27015          * @event initial
27016          * Fire after initEvent
27017          * @param {Roo.bootstrap.DocumentViewer} this
27018          */
27019         "initial" : true,
27020         /**
27021          * @event click
27022          * Fire after click
27023          * @param {Roo.bootstrap.DocumentViewer} this
27024          */
27025         "click" : true,
27026         /**
27027          * @event trash
27028          * Fire after trash button
27029          * @param {Roo.bootstrap.DocumentViewer} this
27030          */
27031         "trash" : true
27032         
27033     });
27034 };
27035
27036 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27037     
27038     getAutoCreate : function()
27039     {
27040         var cfg = {
27041             tag : 'div',
27042             cls : 'roo-document-viewer',
27043             cn : [
27044                 {
27045                     tag : 'div',
27046                     cls : 'roo-document-viewer-body',
27047                     cn : [
27048                         {
27049                             tag : 'div',
27050                             cls : 'roo-document-viewer-thumb',
27051                             cn : [
27052                                 {
27053                                     tag : 'img',
27054                                     cls : 'roo-document-viewer-image'
27055                                 }
27056                             ]
27057                         }
27058                     ]
27059                 },
27060                 {
27061                     tag : 'div',
27062                     cls : 'roo-document-viewer-footer',
27063                     cn : {
27064                         tag : 'div',
27065                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27066                         cn : [
27067                             {
27068                                 tag : 'div',
27069                                 cls : 'btn-group',
27070                                 cn : [
27071                                     {
27072                                         tag : 'button',
27073                                         cls : 'btn btn-default roo-document-viewer-trash',
27074                                         html : '<i class="fa fa-trash"></i>'
27075                                     }
27076                                 ]
27077                             }
27078                         ]
27079                     }
27080                 }
27081             ]
27082         };
27083         
27084         return cfg;
27085     },
27086     
27087     initEvents : function()
27088     {
27089         
27090         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27091         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27092         
27093         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27094         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27095         
27096         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27097         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27098         
27099         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27100         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27101         
27102         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27103         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27104         
27105         this.bodyEl.on('click', this.onClick, this);
27106         
27107         this.trashBtn.on('click', this.onTrash, this);
27108         
27109     },
27110     
27111     initial : function()
27112     {
27113 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27114         
27115         
27116         this.fireEvent('initial', this);
27117         
27118     },
27119     
27120     onClick : function(e)
27121     {
27122         e.preventDefault();
27123         
27124         this.fireEvent('click', this);
27125     },
27126     
27127     onTrash : function(e)
27128     {
27129         e.preventDefault();
27130         
27131         this.fireEvent('trash', this);
27132     }
27133     
27134 });
27135 /*
27136  * - LGPL
27137  *
27138  * nav progress bar
27139  * 
27140  */
27141
27142 /**
27143  * @class Roo.bootstrap.NavProgressBar
27144  * @extends Roo.bootstrap.Component
27145  * Bootstrap NavProgressBar class
27146  * 
27147  * @constructor
27148  * Create a new nav progress bar
27149  * @param {Object} config The config object
27150  */
27151
27152 Roo.bootstrap.NavProgressBar = function(config){
27153     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27154
27155     this.bullets = this.bullets || [];
27156    
27157 //    Roo.bootstrap.NavProgressBar.register(this);
27158      this.addEvents({
27159         /**
27160              * @event changed
27161              * Fires when the active item changes
27162              * @param {Roo.bootstrap.NavProgressBar} this
27163              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27164              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27165          */
27166         'changed': true
27167      });
27168     
27169 };
27170
27171 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27172     
27173     bullets : [],
27174     barItems : [],
27175     
27176     getAutoCreate : function()
27177     {
27178         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27179         
27180         cfg = {
27181             tag : 'div',
27182             cls : 'roo-navigation-bar-group',
27183             cn : [
27184                 {
27185                     tag : 'div',
27186                     cls : 'roo-navigation-top-bar'
27187                 },
27188                 {
27189                     tag : 'div',
27190                     cls : 'roo-navigation-bullets-bar',
27191                     cn : [
27192                         {
27193                             tag : 'ul',
27194                             cls : 'roo-navigation-bar'
27195                         }
27196                     ]
27197                 },
27198                 
27199                 {
27200                     tag : 'div',
27201                     cls : 'roo-navigation-bottom-bar'
27202                 }
27203             ]
27204             
27205         };
27206         
27207         return cfg;
27208         
27209     },
27210     
27211     initEvents: function() 
27212     {
27213         
27214     },
27215     
27216     onRender : function(ct, position) 
27217     {
27218         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27219         
27220         if(this.bullets.length){
27221             Roo.each(this.bullets, function(b){
27222                this.addItem(b);
27223             }, this);
27224         }
27225         
27226         this.format();
27227         
27228     },
27229     
27230     addItem : function(cfg)
27231     {
27232         var item = new Roo.bootstrap.NavProgressItem(cfg);
27233         
27234         item.parentId = this.id;
27235         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27236         
27237         if(cfg.html){
27238             var top = new Roo.bootstrap.Element({
27239                 tag : 'div',
27240                 cls : 'roo-navigation-bar-text'
27241             });
27242             
27243             var bottom = new Roo.bootstrap.Element({
27244                 tag : 'div',
27245                 cls : 'roo-navigation-bar-text'
27246             });
27247             
27248             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27249             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27250             
27251             var topText = new Roo.bootstrap.Element({
27252                 tag : 'span',
27253                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27254             });
27255             
27256             var bottomText = new Roo.bootstrap.Element({
27257                 tag : 'span',
27258                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27259             });
27260             
27261             topText.onRender(top.el, null);
27262             bottomText.onRender(bottom.el, null);
27263             
27264             item.topEl = top;
27265             item.bottomEl = bottom;
27266         }
27267         
27268         this.barItems.push(item);
27269         
27270         return item;
27271     },
27272     
27273     getActive : function()
27274     {
27275         var active = false;
27276         
27277         Roo.each(this.barItems, function(v){
27278             
27279             if (!v.isActive()) {
27280                 return;
27281             }
27282             
27283             active = v;
27284             return false;
27285             
27286         });
27287         
27288         return active;
27289     },
27290     
27291     setActiveItem : function(item)
27292     {
27293         var prev = false;
27294         
27295         Roo.each(this.barItems, function(v){
27296             if (v.rid == item.rid) {
27297                 return ;
27298             }
27299             
27300             if (v.isActive()) {
27301                 v.setActive(false);
27302                 prev = v;
27303             }
27304         });
27305
27306         item.setActive(true);
27307         
27308         this.fireEvent('changed', this, item, prev);
27309     },
27310     
27311     getBarItem: function(rid)
27312     {
27313         var ret = false;
27314         
27315         Roo.each(this.barItems, function(e) {
27316             if (e.rid != rid) {
27317                 return;
27318             }
27319             
27320             ret =  e;
27321             return false;
27322         });
27323         
27324         return ret;
27325     },
27326     
27327     indexOfItem : function(item)
27328     {
27329         var index = false;
27330         
27331         Roo.each(this.barItems, function(v, i){
27332             
27333             if (v.rid != item.rid) {
27334                 return;
27335             }
27336             
27337             index = i;
27338             return false
27339         });
27340         
27341         return index;
27342     },
27343     
27344     setActiveNext : function()
27345     {
27346         var i = this.indexOfItem(this.getActive());
27347         
27348         if (i > this.barItems.length) {
27349             return;
27350         }
27351         
27352         this.setActiveItem(this.barItems[i+1]);
27353     },
27354     
27355     setActivePrev : function()
27356     {
27357         var i = this.indexOfItem(this.getActive());
27358         
27359         if (i  < 1) {
27360             return;
27361         }
27362         
27363         this.setActiveItem(this.barItems[i-1]);
27364     },
27365     
27366     format : function()
27367     {
27368         if(!this.barItems.length){
27369             return;
27370         }
27371      
27372         var width = 100 / this.barItems.length;
27373         
27374         Roo.each(this.barItems, function(i){
27375             i.el.setStyle('width', width + '%');
27376             i.topEl.el.setStyle('width', width + '%');
27377             i.bottomEl.el.setStyle('width', width + '%');
27378         }, this);
27379         
27380     }
27381     
27382 });
27383 /*
27384  * - LGPL
27385  *
27386  * Nav Progress Item
27387  * 
27388  */
27389
27390 /**
27391  * @class Roo.bootstrap.NavProgressItem
27392  * @extends Roo.bootstrap.Component
27393  * Bootstrap NavProgressItem class
27394  * @cfg {String} rid the reference id
27395  * @cfg {Boolean} active (true|false) Is item active default false
27396  * @cfg {Boolean} disabled (true|false) Is item active default false
27397  * @cfg {String} html
27398  * @cfg {String} position (top|bottom) text position default bottom
27399  * @cfg {String} icon show icon instead of number
27400  * 
27401  * @constructor
27402  * Create a new NavProgressItem
27403  * @param {Object} config The config object
27404  */
27405 Roo.bootstrap.NavProgressItem = function(config){
27406     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27407     this.addEvents({
27408         // raw events
27409         /**
27410          * @event click
27411          * The raw click event for the entire grid.
27412          * @param {Roo.bootstrap.NavProgressItem} this
27413          * @param {Roo.EventObject} e
27414          */
27415         "click" : true
27416     });
27417    
27418 };
27419
27420 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27421     
27422     rid : '',
27423     active : false,
27424     disabled : false,
27425     html : '',
27426     position : 'bottom',
27427     icon : false,
27428     
27429     getAutoCreate : function()
27430     {
27431         var iconCls = 'roo-navigation-bar-item-icon';
27432         
27433         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27434         
27435         var cfg = {
27436             tag: 'li',
27437             cls: 'roo-navigation-bar-item',
27438             cn : [
27439                 {
27440                     tag : 'i',
27441                     cls : iconCls
27442                 }
27443             ]
27444         };
27445         
27446         if(this.active){
27447             cfg.cls += ' active';
27448         }
27449         if(this.disabled){
27450             cfg.cls += ' disabled';
27451         }
27452         
27453         return cfg;
27454     },
27455     
27456     disable : function()
27457     {
27458         this.setDisabled(true);
27459     },
27460     
27461     enable : function()
27462     {
27463         this.setDisabled(false);
27464     },
27465     
27466     initEvents: function() 
27467     {
27468         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27469         
27470         this.iconEl.on('click', this.onClick, this);
27471     },
27472     
27473     onClick : function(e)
27474     {
27475         e.preventDefault();
27476         
27477         if(this.disabled){
27478             return;
27479         }
27480         
27481         if(this.fireEvent('click', this, e) === false){
27482             return;
27483         };
27484         
27485         this.parent().setActiveItem(this);
27486     },
27487     
27488     isActive: function () 
27489     {
27490         return this.active;
27491     },
27492     
27493     setActive : function(state)
27494     {
27495         if(this.active == state){
27496             return;
27497         }
27498         
27499         this.active = state;
27500         
27501         if (state) {
27502             this.el.addClass('active');
27503             return;
27504         }
27505         
27506         this.el.removeClass('active');
27507         
27508         return;
27509     },
27510     
27511     setDisabled : function(state)
27512     {
27513         if(this.disabled == state){
27514             return;
27515         }
27516         
27517         this.disabled = state;
27518         
27519         if (state) {
27520             this.el.addClass('disabled');
27521             return;
27522         }
27523         
27524         this.el.removeClass('disabled');
27525     },
27526     
27527     tooltipEl : function()
27528     {
27529         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27530     }
27531 });
27532  
27533
27534  /*
27535  * - LGPL
27536  *
27537  * FieldLabel
27538  * 
27539  */
27540
27541 /**
27542  * @class Roo.bootstrap.FieldLabel
27543  * @extends Roo.bootstrap.Component
27544  * Bootstrap FieldLabel class
27545  * @cfg {String} html contents of the element
27546  * @cfg {String} tag tag of the element default label
27547  * @cfg {String} cls class of the element
27548  * @cfg {String} target label target 
27549  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27550  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27551  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27552  * @cfg {String} iconTooltip default "This field is required"
27553  * 
27554  * @constructor
27555  * Create a new FieldLabel
27556  * @param {Object} config The config object
27557  */
27558
27559 Roo.bootstrap.FieldLabel = function(config){
27560     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27561     
27562     this.addEvents({
27563             /**
27564              * @event invalid
27565              * Fires after the field has been marked as invalid.
27566              * @param {Roo.form.FieldLabel} this
27567              * @param {String} msg The validation message
27568              */
27569             invalid : true,
27570             /**
27571              * @event valid
27572              * Fires after the field has been validated with no errors.
27573              * @param {Roo.form.FieldLabel} this
27574              */
27575             valid : true
27576         });
27577 };
27578
27579 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27580     
27581     tag: 'label',
27582     cls: '',
27583     html: '',
27584     target: '',
27585     allowBlank : true,
27586     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27587     validClass : 'text-success fa fa-lg fa-check',
27588     iconTooltip : 'This field is required',
27589     
27590     getAutoCreate : function(){
27591         
27592         var cfg = {
27593             tag : this.tag,
27594             cls : 'roo-bootstrap-field-label ' + this.cls,
27595             for : this.target,
27596             cn : [
27597                 {
27598                     tag : 'i',
27599                     cls : '',
27600                     tooltip : this.iconTooltip
27601                 },
27602                 {
27603                     tag : 'span',
27604                     html : this.html
27605                 }
27606             ] 
27607         };
27608         
27609         return cfg;
27610     },
27611     
27612     initEvents: function() 
27613     {
27614         Roo.bootstrap.Element.superclass.initEvents.call(this);
27615         
27616         this.iconEl = this.el.select('i', true).first();
27617         
27618         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27619         
27620         Roo.bootstrap.FieldLabel.register(this);
27621     },
27622     
27623     /**
27624      * Mark this field as valid
27625      */
27626     markValid : function()
27627     {
27628         this.iconEl.show();
27629         
27630         this.iconEl.removeClass(this.invalidClass);
27631         
27632         this.iconEl.addClass(this.validClass);
27633         
27634         this.fireEvent('valid', this);
27635     },
27636     
27637     /**
27638      * Mark this field as invalid
27639      * @param {String} msg The validation message
27640      */
27641     markInvalid : function(msg)
27642     {
27643         this.iconEl.show();
27644         
27645         this.iconEl.removeClass(this.validClass);
27646         
27647         this.iconEl.addClass(this.invalidClass);
27648         
27649         this.fireEvent('invalid', this, msg);
27650     }
27651     
27652    
27653 });
27654
27655 Roo.apply(Roo.bootstrap.FieldLabel, {
27656     
27657     groups: {},
27658     
27659      /**
27660     * register a FieldLabel Group
27661     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27662     */
27663     register : function(label)
27664     {
27665         if(this.groups.hasOwnProperty(label.target)){
27666             return;
27667         }
27668      
27669         this.groups[label.target] = label;
27670         
27671     },
27672     /**
27673     * fetch a FieldLabel Group based on the target
27674     * @param {string} target
27675     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27676     */
27677     get: function(target) {
27678         if (typeof(this.groups[target]) == 'undefined') {
27679             return false;
27680         }
27681         
27682         return this.groups[target] ;
27683     }
27684 });
27685
27686  
27687
27688  /*
27689  * - LGPL
27690  *
27691  * page DateSplitField.
27692  * 
27693  */
27694
27695
27696 /**
27697  * @class Roo.bootstrap.DateSplitField
27698  * @extends Roo.bootstrap.Component
27699  * Bootstrap DateSplitField class
27700  * @cfg {string} fieldLabel - the label associated
27701  * @cfg {Number} labelWidth set the width of label (0-12)
27702  * @cfg {String} labelAlign (top|left)
27703  * @cfg {Boolean} dayAllowBlank (true|false) default false
27704  * @cfg {Boolean} monthAllowBlank (true|false) default false
27705  * @cfg {Boolean} yearAllowBlank (true|false) default false
27706  * @cfg {string} dayPlaceholder 
27707  * @cfg {string} monthPlaceholder
27708  * @cfg {string} yearPlaceholder
27709  * @cfg {string} dayFormat default 'd'
27710  * @cfg {string} monthFormat default 'm'
27711  * @cfg {string} yearFormat default 'Y'
27712
27713  *     
27714  * @constructor
27715  * Create a new DateSplitField
27716  * @param {Object} config The config object
27717  */
27718
27719 Roo.bootstrap.DateSplitField = function(config){
27720     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27721     
27722     this.addEvents({
27723         // raw events
27724          /**
27725          * @event years
27726          * getting the data of years
27727          * @param {Roo.bootstrap.DateSplitField} this
27728          * @param {Object} years
27729          */
27730         "years" : true,
27731         /**
27732          * @event days
27733          * getting the data of days
27734          * @param {Roo.bootstrap.DateSplitField} this
27735          * @param {Object} days
27736          */
27737         "days" : true,
27738         /**
27739          * @event invalid
27740          * Fires after the field has been marked as invalid.
27741          * @param {Roo.form.Field} this
27742          * @param {String} msg The validation message
27743          */
27744         invalid : true,
27745        /**
27746          * @event valid
27747          * Fires after the field has been validated with no errors.
27748          * @param {Roo.form.Field} this
27749          */
27750         valid : true
27751     });
27752 };
27753
27754 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27755     
27756     fieldLabel : '',
27757     labelAlign : 'top',
27758     labelWidth : 3,
27759     dayAllowBlank : false,
27760     monthAllowBlank : false,
27761     yearAllowBlank : false,
27762     dayPlaceholder : '',
27763     monthPlaceholder : '',
27764     yearPlaceholder : '',
27765     dayFormat : 'd',
27766     monthFormat : 'm',
27767     yearFormat : 'Y',
27768     isFormField : true,
27769     
27770     getAutoCreate : function()
27771     {
27772         var cfg = {
27773             tag : 'div',
27774             cls : 'row roo-date-split-field-group',
27775             cn : [
27776                 {
27777                     tag : 'input',
27778                     type : 'hidden',
27779                     cls : 'form-hidden-field roo-date-split-field-group-value',
27780                     name : this.name
27781                 }
27782             ]
27783         };
27784         
27785         if(this.fieldLabel){
27786             cfg.cn.push({
27787                 tag : 'div',
27788                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27789                 cn : [
27790                     {
27791                         tag : 'label',
27792                         html : this.fieldLabel
27793                     }
27794                 ]
27795             });
27796         }
27797         
27798         Roo.each(['day', 'month', 'year'], function(t){
27799             cfg.cn.push({
27800                 tag : 'div',
27801                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27802             });
27803         }, this);
27804         
27805         return cfg;
27806     },
27807     
27808     inputEl: function ()
27809     {
27810         return this.el.select('.roo-date-split-field-group-value', true).first();
27811     },
27812     
27813     onRender : function(ct, position) 
27814     {
27815         var _this = this;
27816         
27817         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27818         
27819         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27820         
27821         this.dayField = new Roo.bootstrap.ComboBox({
27822             allowBlank : this.dayAllowBlank,
27823             alwaysQuery : true,
27824             displayField : 'value',
27825             editable : false,
27826             fieldLabel : '',
27827             forceSelection : true,
27828             mode : 'local',
27829             placeholder : this.dayPlaceholder,
27830             selectOnFocus : true,
27831             tpl : '<div class="select2-result"><b>{value}</b></div>',
27832             triggerAction : 'all',
27833             typeAhead : true,
27834             valueField : 'value',
27835             store : new Roo.data.SimpleStore({
27836                 data : (function() {    
27837                     var days = [];
27838                     _this.fireEvent('days', _this, days);
27839                     return days;
27840                 })(),
27841                 fields : [ 'value' ]
27842             }),
27843             listeners : {
27844                 select : function (_self, record, index)
27845                 {
27846                     _this.setValue(_this.getValue());
27847                 }
27848             }
27849         });
27850
27851         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27852         
27853         this.monthField = new Roo.bootstrap.MonthField({
27854             after : '<i class=\"fa fa-calendar\"></i>',
27855             allowBlank : this.monthAllowBlank,
27856             placeholder : this.monthPlaceholder,
27857             readOnly : true,
27858             listeners : {
27859                 render : function (_self)
27860                 {
27861                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27862                         e.preventDefault();
27863                         _self.focus();
27864                     });
27865                 },
27866                 select : function (_self, oldvalue, newvalue)
27867                 {
27868                     _this.setValue(_this.getValue());
27869                 }
27870             }
27871         });
27872         
27873         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27874         
27875         this.yearField = new Roo.bootstrap.ComboBox({
27876             allowBlank : this.yearAllowBlank,
27877             alwaysQuery : true,
27878             displayField : 'value',
27879             editable : false,
27880             fieldLabel : '',
27881             forceSelection : true,
27882             mode : 'local',
27883             placeholder : this.yearPlaceholder,
27884             selectOnFocus : true,
27885             tpl : '<div class="select2-result"><b>{value}</b></div>',
27886             triggerAction : 'all',
27887             typeAhead : true,
27888             valueField : 'value',
27889             store : new Roo.data.SimpleStore({
27890                 data : (function() {
27891                     var years = [];
27892                     _this.fireEvent('years', _this, years);
27893                     return years;
27894                 })(),
27895                 fields : [ 'value' ]
27896             }),
27897             listeners : {
27898                 select : function (_self, record, index)
27899                 {
27900                     _this.setValue(_this.getValue());
27901                 }
27902             }
27903         });
27904
27905         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27906     },
27907     
27908     setValue : function(v, format)
27909     {
27910         this.inputEl.dom.value = v;
27911         
27912         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27913         
27914         var d = Date.parseDate(v, f);
27915         
27916         if(!d){
27917             this.validate();
27918             return;
27919         }
27920         
27921         this.setDay(d.format(this.dayFormat));
27922         this.setMonth(d.format(this.monthFormat));
27923         this.setYear(d.format(this.yearFormat));
27924         
27925         this.validate();
27926         
27927         return;
27928     },
27929     
27930     setDay : function(v)
27931     {
27932         this.dayField.setValue(v);
27933         this.inputEl.dom.value = this.getValue();
27934         this.validate();
27935         return;
27936     },
27937     
27938     setMonth : function(v)
27939     {
27940         this.monthField.setValue(v, true);
27941         this.inputEl.dom.value = this.getValue();
27942         this.validate();
27943         return;
27944     },
27945     
27946     setYear : function(v)
27947     {
27948         this.yearField.setValue(v);
27949         this.inputEl.dom.value = this.getValue();
27950         this.validate();
27951         return;
27952     },
27953     
27954     getDay : function()
27955     {
27956         return this.dayField.getValue();
27957     },
27958     
27959     getMonth : function()
27960     {
27961         return this.monthField.getValue();
27962     },
27963     
27964     getYear : function()
27965     {
27966         return this.yearField.getValue();
27967     },
27968     
27969     getValue : function()
27970     {
27971         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
27972         
27973         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
27974         
27975         return date;
27976     },
27977     
27978     reset : function()
27979     {
27980         this.setDay('');
27981         this.setMonth('');
27982         this.setYear('');
27983         this.inputEl.dom.value = '';
27984         this.validate();
27985         return;
27986     },
27987     
27988     validate : function()
27989     {
27990         var d = this.dayField.validate();
27991         var m = this.monthField.validate();
27992         var y = this.yearField.validate();
27993         
27994         var valid = true;
27995         
27996         if(
27997                 (!this.dayAllowBlank && !d) ||
27998                 (!this.monthAllowBlank && !m) ||
27999                 (!this.yearAllowBlank && !y)
28000         ){
28001             valid = false;
28002         }
28003         
28004         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28005             return valid;
28006         }
28007         
28008         if(valid){
28009             this.markValid();
28010             return valid;
28011         }
28012         
28013         this.markInvalid();
28014         
28015         return valid;
28016     },
28017     
28018     markValid : function()
28019     {
28020         
28021         var label = this.el.select('label', true).first();
28022         var icon = this.el.select('i.fa-star', true).first();
28023
28024         if(label && icon){
28025             icon.remove();
28026         }
28027         
28028         this.fireEvent('valid', this);
28029     },
28030     
28031      /**
28032      * Mark this field as invalid
28033      * @param {String} msg The validation message
28034      */
28035     markInvalid : function(msg)
28036     {
28037         
28038         var label = this.el.select('label', true).first();
28039         var icon = this.el.select('i.fa-star', true).first();
28040
28041         if(label && !icon){
28042             this.el.select('.roo-date-split-field-label', true).createChild({
28043                 tag : 'i',
28044                 cls : 'text-danger fa fa-lg fa-star',
28045                 tooltip : 'This field is required',
28046                 style : 'margin-right:5px;'
28047             }, label, true);
28048         }
28049         
28050         this.fireEvent('invalid', this, msg);
28051     },
28052     
28053     clearInvalid : function()
28054     {
28055         var label = this.el.select('label', true).first();
28056         var icon = this.el.select('i.fa-star', true).first();
28057
28058         if(label && icon){
28059             icon.remove();
28060         }
28061         
28062         this.fireEvent('valid', this);
28063     },
28064     
28065     getName: function()
28066     {
28067         return this.name;
28068     }
28069     
28070 });
28071
28072