sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         this.initEvents();
140         
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165            
166         cn.parentType = this.xtype; //??
167         cn.parentId = this.id;
168         
169         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
170         if (typeof(cn.container_method) == 'string') {
171             cntr = cn.container_method;
172         }
173         
174         
175         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
176         
177         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
178         
179         var build_from_html =  Roo.XComponent.build_from_html;
180           
181         var is_body  = (tree.xtype == 'Body') ;
182           
183         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
184           
185         var self_cntr_el = Roo.get(this[cntr](false));
186         
187         // do not try and build conditional elements 
188         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
189             return false;
190         }
191         
192         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
193             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
194                 return this.addxtypeChild(tree,cntr, is_body);
195             }
196             
197             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
198                 
199             if(echild){
200                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
201             }
202             
203             Roo.log('skipping render');
204             return cn;
205             
206         }
207         
208         var ret = false;
209         if (!build_from_html) {
210             return false;
211         }
212         
213         // this i think handles overlaying multiple children of the same type
214         // with the sam eelement.. - which might be buggy..
215         while (true) {
216             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
217             
218             if (!echild) {
219                 break;
220             }
221             
222             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
223                 break;
224             }
225             
226             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
227         }
228         return ret;
229     },
230     
231     addxtypeChild : function (tree, cntr, is_body)
232     {
233         Roo.debug && Roo.log('addxtypeChild:' + cntr);
234         var cn = this;
235         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
236         
237         
238         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
239                     (typeof(tree['flexy:foreach']) != 'undefined');
240           
241         
242         
243          skip_children = false;
244         // render the element if it's not BODY.
245         if (!is_body) {
246            
247             cn = Roo.factory(tree);
248            
249             cn.parentType = this.xtype; //??
250             cn.parentId = this.id;
251             
252             var build_from_html =  Roo.XComponent.build_from_html;
253             
254             
255             // does the container contain child eleemnts with 'xtype' attributes.
256             // that match this xtype..
257             // note - when we render we create these as well..
258             // so we should check to see if body has xtype set.
259             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
260                
261                 var self_cntr_el = Roo.get(this[cntr](false));
262                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
263                 if (echild) { 
264                     //Roo.log(Roo.XComponent.build_from_html);
265                     //Roo.log("got echild:");
266                     //Roo.log(echild);
267                 }
268                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
269                 // and are not displayed -this causes this to use up the wrong element when matching.
270                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
271                 
272                 
273                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
274                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
275                   
276                   
277                   
278                     cn.el = echild;
279                   //  Roo.log("GOT");
280                     //echild.dom.removeAttribute('xtype');
281                 } else {
282                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
283                     Roo.debug && Roo.log(self_cntr_el);
284                     Roo.debug && Roo.log(echild);
285                     Roo.debug && Roo.log(cn);
286                 }
287             }
288            
289             
290            
291             // if object has flexy:if - then it may or may not be rendered.
292             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
293                 // skip a flexy if element.
294                 Roo.debug && Roo.log('skipping render');
295                 Roo.debug && Roo.log(tree);
296                 if (!cn.el) {
297                     Roo.debug && Roo.log('skipping all children');
298                     skip_children = true;
299                 }
300                 
301              } else {
302                  
303                 // actually if flexy:foreach is found, we really want to create 
304                 // multiple copies here...
305                 //Roo.log('render');
306                 //Roo.log(this[cntr]());
307                 cn.render(this[cntr](true));
308              }
309             // then add the element..
310         }
311         
312         
313         // handle the kids..
314         
315         var nitems = [];
316         /*
317         if (typeof (tree.menu) != 'undefined') {
318             tree.menu.parentType = cn.xtype;
319             tree.menu.triggerEl = cn.el;
320             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
321             
322         }
323         */
324         if (!tree.items || !tree.items.length) {
325             cn.items = nitems;
326             return cn;
327         }
328         var items = tree.items;
329         delete tree.items;
330         
331         //Roo.log(items.length);
332             // add the items..
333         if (!skip_children) {    
334             for(var i =0;i < items.length;i++) {
335                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
336             }
337         }
338         
339         cn.items = nitems;
340         
341         this.fireEvent('childrenrendered', this);
342         
343         return cn;
344     },
345     /**
346      * Show a component - removes 'hidden' class
347      */
348     show : function()
349     {
350         if (this.el) {
351             this.el.removeClass('hidden');
352         }
353     },
354     /**
355      * Hide a component - adds 'hidden' class
356      */
357     hide: function()
358     {
359         if (this.el && !this.el.hasClass('hidden')) {
360             this.el.addClass('hidden');
361         }
362         
363     }
364 });
365
366  /*
367  * - LGPL
368  *
369  * Body
370  * 
371  */
372
373 /**
374  * @class Roo.bootstrap.Body
375  * @extends Roo.bootstrap.Component
376  * Bootstrap Body class
377  * 
378  * @constructor
379  * Create a new body
380  * @param {Object} config The config object
381  */
382
383 Roo.bootstrap.Body = function(config){
384     Roo.bootstrap.Body.superclass.constructor.call(this, config);
385     this.el = Roo.get(document.body);
386     if (this.cls && this.cls.length) {
387         Roo.get(document.body).addClass(this.cls);
388     }
389 };
390
391 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
392     
393     is_body : true,// just to make sure it's constructed?
394     
395         autoCreate : {
396         cls: 'container'
397     },
398     onRender : function(ct, position)
399     {
400        /* Roo.log("Roo.bootstrap.Body - onRender");
401         if (this.cls && this.cls.length) {
402             Roo.get(document.body).addClass(this.cls);
403         }
404         // style??? xttr???
405         */
406     }
407     
408     
409  
410    
411 });
412
413  /*
414  * - LGPL
415  *
416  * button group
417  * 
418  */
419
420
421 /**
422  * @class Roo.bootstrap.ButtonGroup
423  * @extends Roo.bootstrap.Component
424  * Bootstrap ButtonGroup class
425  * @cfg {String} size lg | sm | xs (default empty normal)
426  * @cfg {String} align vertical | justified  (default none)
427  * @cfg {String} direction up | down (default down)
428  * @cfg {Boolean} toolbar false | true
429  * @cfg {Boolean} btn true | false
430  * 
431  * 
432  * @constructor
433  * Create a new Input
434  * @param {Object} config The config object
435  */
436
437 Roo.bootstrap.ButtonGroup = function(config){
438     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
439 };
440
441 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
442     
443     size: '',
444     align: '',
445     direction: '',
446     toolbar: false,
447     btn: true,
448
449     getAutoCreate : function(){
450         var cfg = {
451             cls: 'btn-group',
452             html : null
453         };
454         
455         cfg.html = this.html || cfg.html;
456         
457         if (this.toolbar) {
458             cfg = {
459                 cls: 'btn-toolbar',
460                 html: null
461             };
462             
463             return cfg;
464         }
465         
466         if (['vertical','justified'].indexOf(this.align)!==-1) {
467             cfg.cls = 'btn-group-' + this.align;
468             
469             if (this.align == 'justified') {
470                 console.log(this.items);
471             }
472         }
473         
474         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
475             cfg.cls += ' btn-group-' + this.size;
476         }
477         
478         if (this.direction == 'up') {
479             cfg.cls += ' dropup' ;
480         }
481         
482         return cfg;
483     }
484    
485 });
486
487  /*
488  * - LGPL
489  *
490  * button
491  * 
492  */
493
494 /**
495  * @class Roo.bootstrap.Button
496  * @extends Roo.bootstrap.Component
497  * Bootstrap Button class
498  * @cfg {String} html The button content
499  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
500  * @cfg {String} size ( lg | sm | xs)
501  * @cfg {String} tag ( a | input | submit)
502  * @cfg {String} href empty or href
503  * @cfg {Boolean} disabled default false;
504  * @cfg {Boolean} isClose default false;
505  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
506  * @cfg {String} badge text for badge
507  * @cfg {String} theme default 
508  * @cfg {Boolean} inverse 
509  * @cfg {Boolean} toggle 
510  * @cfg {String} ontext text for on toggle state
511  * @cfg {String} offtext text for off toggle state
512  * @cfg {Boolean} defaulton 
513  * @cfg {Boolean} preventDefault  default true
514  * @cfg {Boolean} removeClass remove the standard class..
515  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
516  * 
517  * @constructor
518  * Create a new button
519  * @param {Object} config The config object
520  */
521
522
523 Roo.bootstrap.Button = function(config){
524     Roo.bootstrap.Button.superclass.constructor.call(this, config);
525     this.addEvents({
526         // raw events
527         /**
528          * @event click
529          * When a butotn is pressed
530          * @param {Roo.bootstrap.Button} this
531          * @param {Roo.EventObject} e
532          */
533         "click" : true,
534          /**
535          * @event toggle
536          * After the button has been toggles
537          * @param {Roo.EventObject} e
538          * @param {boolean} pressed (also available as button.pressed)
539          */
540         "toggle" : true
541     });
542 };
543
544 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
545     html: false,
546     active: false,
547     weight: '',
548     size: '',
549     tag: 'button',
550     href: '',
551     disabled: false,
552     isClose: false,
553     glyphicon: '',
554     badge: '',
555     theme: 'default',
556     inverse: false,
557     
558     toggle: false,
559     ontext: 'ON',
560     offtext: 'OFF',
561     defaulton: true,
562     preventDefault: true,
563     removeClass: false,
564     name: false,
565     target: false,
566     
567     
568     pressed : null,
569      
570     
571     getAutoCreate : function(){
572         
573         var cfg = {
574             tag : 'button',
575             cls : 'roo-button',
576             html: ''
577         };
578         
579         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
580             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
581             this.tag = 'button';
582         } else {
583             cfg.tag = this.tag;
584         }
585         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
586         
587         if (this.toggle == true) {
588             cfg={
589                 tag: 'div',
590                 cls: 'slider-frame roo-button',
591                 cn: [
592                     {
593                         tag: 'span',
594                         'data-on-text':'ON',
595                         'data-off-text':'OFF',
596                         cls: 'slider-button',
597                         html: this.offtext
598                     }
599                 ]
600             };
601             
602             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
603                 cfg.cls += ' '+this.weight;
604             }
605             
606             return cfg;
607         }
608         
609         if (this.isClose) {
610             cfg.cls += ' close';
611             
612             cfg["aria-hidden"] = true;
613             
614             cfg.html = "&times;";
615             
616             return cfg;
617         }
618         
619          
620         if (this.theme==='default') {
621             cfg.cls = 'btn roo-button';
622             
623             //if (this.parentType != 'Navbar') {
624             this.weight = this.weight.length ?  this.weight : 'default';
625             //}
626             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
627                 
628                 cfg.cls += ' btn-' + this.weight;
629             }
630         } else if (this.theme==='glow') {
631             
632             cfg.tag = 'a';
633             cfg.cls = 'btn-glow roo-button';
634             
635             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
636                 
637                 cfg.cls += ' ' + this.weight;
638             }
639         }
640    
641         
642         if (this.inverse) {
643             this.cls += ' inverse';
644         }
645         
646         
647         if (this.active) {
648             cfg.cls += ' active';
649         }
650         
651         if (this.disabled) {
652             cfg.disabled = 'disabled';
653         }
654         
655         if (this.items) {
656             Roo.log('changing to ul' );
657             cfg.tag = 'ul';
658             this.glyphicon = 'caret';
659         }
660         
661         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
662          
663         //gsRoo.log(this.parentType);
664         if (this.parentType === 'Navbar' && !this.parent().bar) {
665             Roo.log('changing to li?');
666             
667             cfg.tag = 'li';
668             
669             cfg.cls = '';
670             cfg.cn =  [{
671                 tag : 'a',
672                 cls : 'roo-button',
673                 html : this.html,
674                 href : this.href || '#'
675             }];
676             if (this.menu) {
677                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
678                 cfg.cls += ' dropdown';
679             }   
680             
681             delete cfg.html;
682             
683         }
684         
685        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
686         
687         if (this.glyphicon) {
688             cfg.html = ' ' + cfg.html;
689             
690             cfg.cn = [
691                 {
692                     tag: 'span',
693                     cls: 'glyphicon glyphicon-' + this.glyphicon
694                 }
695             ];
696         }
697         
698         if (this.badge) {
699             cfg.html += ' ';
700             
701             cfg.tag = 'a';
702             
703 //            cfg.cls='btn roo-button';
704             
705             cfg.href=this.href;
706             
707             var value = cfg.html;
708             
709             if(this.glyphicon){
710                 value = {
711                             tag: 'span',
712                             cls: 'glyphicon glyphicon-' + this.glyphicon,
713                             html: this.html
714                         };
715                 
716             }
717             
718             cfg.cn = [
719                 value,
720                 {
721                     tag: 'span',
722                     cls: 'badge',
723                     html: this.badge
724                 }
725             ];
726             
727             cfg.html='';
728         }
729         
730         if (this.menu) {
731             cfg.cls += ' dropdown';
732             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
733         }
734         
735         if (cfg.tag !== 'a' && this.href !== '') {
736             throw "Tag must be a to set href.";
737         } else if (this.href.length > 0) {
738             cfg.href = this.href;
739         }
740         
741         if(this.removeClass){
742             cfg.cls = '';
743         }
744         
745         if(this.target){
746             cfg.target = this.target;
747         }
748         
749         return cfg;
750     },
751     initEvents: function() {
752        // Roo.log('init events?');
753 //        Roo.log(this.el.dom);
754         // add the menu...
755         
756         if (typeof (this.menu) != 'undefined') {
757             this.menu.parentType = this.xtype;
758             this.menu.triggerEl = this.el;
759             this.addxtype(Roo.apply({}, this.menu));
760         }
761
762
763        if (this.el.hasClass('roo-button')) {
764             this.el.on('click', this.onClick, this);
765        } else {
766             this.el.select('.roo-button').on('click', this.onClick, this);
767        }
768        
769        if(this.removeClass){
770            this.el.on('click', this.onClick, this);
771        }
772        
773        this.el.enableDisplayMode();
774         
775     },
776     onClick : function(e)
777     {
778         if (this.disabled) {
779             return;
780         }
781         
782         
783         Roo.log('button on click ');
784         if(this.preventDefault){
785             e.preventDefault();
786         }
787         if (this.pressed === true || this.pressed === false) {
788             this.pressed = !this.pressed;
789             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
790             this.fireEvent('toggle', this, e, this.pressed);
791         }
792         
793         
794         this.fireEvent('click', this, e);
795     },
796     
797     /**
798      * Enables this button
799      */
800     enable : function()
801     {
802         this.disabled = false;
803         this.el.removeClass('disabled');
804     },
805     
806     /**
807      * Disable this button
808      */
809     disable : function()
810     {
811         this.disabled = true;
812         this.el.addClass('disabled');
813     },
814      /**
815      * sets the active state on/off, 
816      * @param {Boolean} state (optional) Force a particular state
817      */
818     setActive : function(v) {
819         
820         this.el[v ? 'addClass' : 'removeClass']('active');
821     },
822      /**
823      * toggles the current active state 
824      */
825     toggleActive : function()
826     {
827        var active = this.el.hasClass('active');
828        this.setActive(!active);
829        
830         
831     },
832     setText : function(str)
833     {
834         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
835     },
836     getText : function()
837     {
838         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
839     },
840     hide: function() {
841        
842      
843         this.el.hide();   
844     },
845     show: function() {
846        
847         this.el.show();   
848     }
849     
850     
851 });
852
853  /*
854  * - LGPL
855  *
856  * column
857  * 
858  */
859
860 /**
861  * @class Roo.bootstrap.Column
862  * @extends Roo.bootstrap.Component
863  * Bootstrap Column class
864  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
865  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
866  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
867  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
868  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
869  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
870  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
871  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
872  *
873  * 
874  * @cfg {Boolean} hidden (true|false) hide the element
875  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
876  * @cfg {String} fa (ban|check|...) font awesome icon
877  * @cfg {Number} fasize (1|2|....) font awsome size
878
879  * @cfg {String} icon (info-sign|check|...) glyphicon name
880
881  * @cfg {String} html content of column.
882  * 
883  * @constructor
884  * Create a new Column
885  * @param {Object} config The config object
886  */
887
888 Roo.bootstrap.Column = function(config){
889     Roo.bootstrap.Column.superclass.constructor.call(this, config);
890 };
891
892 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
893     
894     xs: false,
895     sm: false,
896     md: false,
897     lg: false,
898     xsoff: false,
899     smoff: false,
900     mdoff: false,
901     lgoff: false,
902     html: '',
903     offset: 0,
904     alert: false,
905     fa: false,
906     icon : false,
907     hidden : false,
908     fasize : 1,
909     
910     getAutoCreate : function(){
911         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
912         
913         cfg = {
914             tag: 'div',
915             cls: 'column'
916         };
917         
918         var settings=this;
919         ['xs','sm','md','lg'].map(function(size){
920             //Roo.log( size + ':' + settings[size]);
921             
922             if (settings[size+'off'] !== false) {
923                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
924             }
925             
926             if (settings[size] === false) {
927                 return;
928             }
929             
930             if (!settings[size]) { // 0 = hidden
931                 cfg.cls += ' hidden-' + size;
932                 return;
933             }
934             cfg.cls += ' col-' + size + '-' + settings[size];
935             
936         });
937         
938         if (this.hidden) {
939             cfg.cls += ' hidden';
940         }
941         
942         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
943             cfg.cls +=' alert alert-' + this.alert;
944         }
945         
946         
947         if (this.html.length) {
948             cfg.html = this.html;
949         }
950         if (this.fa) {
951             var fasize = '';
952             if (this.fasize > 1) {
953                 fasize = ' fa-' + this.fasize + 'x';
954             }
955             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
956             
957             
958         }
959         if (this.icon) {
960             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
961         }
962         
963         return cfg;
964     }
965    
966 });
967
968  
969
970  /*
971  * - LGPL
972  *
973  * page container.
974  * 
975  */
976
977
978 /**
979  * @class Roo.bootstrap.Container
980  * @extends Roo.bootstrap.Component
981  * Bootstrap Container class
982  * @cfg {Boolean} jumbotron is it a jumbotron element
983  * @cfg {String} html content of element
984  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
985  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
986  * @cfg {String} header content of header (for panel)
987  * @cfg {String} footer content of footer (for panel)
988  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
989  * @cfg {String} tag (header|aside|section) type of HTML tag.
990  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
991  * @cfg {String} fa font awesome icon
992  * @cfg {String} icon (info-sign|check|...) glyphicon name
993  * @cfg {Boolean} hidden (true|false) hide the element
994  * @cfg {Boolean} expandable (true|false) default false
995  * @cfg {Boolean} expanded (true|false) default true
996  * @cfg {String} rheader contet on the right of header
997  * @cfg {Boolean} clickable (true|false) default false
998
999  *     
1000  * @constructor
1001  * Create a new Container
1002  * @param {Object} config The config object
1003  */
1004
1005 Roo.bootstrap.Container = function(config){
1006     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1007     
1008     this.addEvents({
1009         // raw events
1010          /**
1011          * @event expand
1012          * After the panel has been expand
1013          * 
1014          * @param {Roo.bootstrap.Container} this
1015          */
1016         "expand" : true,
1017         /**
1018          * @event collapse
1019          * After the panel has been collapsed
1020          * 
1021          * @param {Roo.bootstrap.Container} this
1022          */
1023         "collapse" : true,
1024         /**
1025          * @event click
1026          * When a element is chick
1027          * @param {Roo.bootstrap.Container} this
1028          * @param {Roo.EventObject} e
1029          */
1030         "click" : true
1031     });
1032 };
1033
1034 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1035     
1036     jumbotron : false,
1037     well: '',
1038     panel : '',
1039     header: '',
1040     footer : '',
1041     sticky: '',
1042     tag : false,
1043     alert : false,
1044     fa: false,
1045     icon : false,
1046     expandable : false,
1047     rheader : '',
1048     expanded : true,
1049     clickable: false,
1050   
1051      
1052     getChildContainer : function() {
1053         
1054         if(!this.el){
1055             return false;
1056         }
1057         
1058         if (this.panel.length) {
1059             return this.el.select('.panel-body',true).first();
1060         }
1061         
1062         return this.el;
1063     },
1064     
1065     
1066     getAutoCreate : function(){
1067         
1068         var cfg = {
1069             tag : this.tag || 'div',
1070             html : '',
1071             cls : ''
1072         };
1073         if (this.jumbotron) {
1074             cfg.cls = 'jumbotron';
1075         }
1076         
1077         
1078         
1079         // - this is applied by the parent..
1080         //if (this.cls) {
1081         //    cfg.cls = this.cls + '';
1082         //}
1083         
1084         if (this.sticky.length) {
1085             
1086             var bd = Roo.get(document.body);
1087             if (!bd.hasClass('bootstrap-sticky')) {
1088                 bd.addClass('bootstrap-sticky');
1089                 Roo.select('html',true).setStyle('height', '100%');
1090             }
1091              
1092             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1093         }
1094         
1095         
1096         if (this.well.length) {
1097             switch (this.well) {
1098                 case 'lg':
1099                 case 'sm':
1100                     cfg.cls +=' well well-' +this.well;
1101                     break;
1102                 default:
1103                     cfg.cls +=' well';
1104                     break;
1105             }
1106         }
1107         
1108         if (this.hidden) {
1109             cfg.cls += ' hidden';
1110         }
1111         
1112         
1113         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1114             cfg.cls +=' alert alert-' + this.alert;
1115         }
1116         
1117         var body = cfg;
1118         
1119         if (this.panel.length) {
1120             cfg.cls += ' panel panel-' + this.panel;
1121             cfg.cn = [];
1122             if (this.header.length) {
1123                 
1124                 var h = [];
1125                 
1126                 if(this.expandable){
1127                     
1128                     cfg.cls = cfg.cls + ' expandable';
1129                     
1130                     h.push({
1131                         tag: 'i',
1132                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1133                     });
1134                     
1135                 }
1136                 
1137                 h.push(
1138                     {
1139                         tag: 'span',
1140                         cls : 'panel-title',
1141                         html : (this.expandable ? '&nbsp;' : '') + this.header
1142                     },
1143                     {
1144                         tag: 'span',
1145                         cls: 'panel-header-right',
1146                         html: this.rheader
1147                     }
1148                 );
1149                 
1150                 cfg.cn.push({
1151                     cls : 'panel-heading',
1152                     style : this.expandable ? 'cursor: pointer' : '',
1153                     cn : h
1154                 });
1155                 
1156             }
1157             
1158             body = false;
1159             cfg.cn.push({
1160                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1161                 html : this.html
1162             });
1163             
1164             
1165             if (this.footer.length) {
1166                 cfg.cn.push({
1167                     cls : 'panel-footer',
1168                     html : this.footer
1169                     
1170                 });
1171             }
1172             
1173         }
1174         
1175         if (body) {
1176             body.html = this.html || cfg.html;
1177             // prefix with the icons..
1178             if (this.fa) {
1179                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1180             }
1181             if (this.icon) {
1182                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1183             }
1184             
1185             
1186         }
1187         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1188             cfg.cls =  'container';
1189         }
1190         
1191         return cfg;
1192     },
1193     
1194     initEvents: function() 
1195     {
1196         if(this.expandable){
1197             var headerEl = this.headerEl();
1198         
1199             if(headerEl){
1200                 headerEl.on('click', this.onToggleClick, this);
1201             }
1202         }
1203         
1204         if(this.clickable){
1205             this.el.on('click', this.onClick, this);
1206         }
1207         
1208     },
1209     
1210     onToggleClick : function()
1211     {
1212         var headerEl = this.headerEl();
1213         
1214         if(!headerEl){
1215             return;
1216         }
1217         
1218         if(this.expanded){
1219             this.collapse();
1220             return;
1221         }
1222         
1223         this.expand();
1224     },
1225     
1226     expand : function()
1227     {
1228         if(this.fireEvent('expand', this)) {
1229             
1230             this.expanded = true;
1231             
1232             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1233             
1234             this.el.select('.panel-body',true).first().removeClass('hide');
1235             
1236             var toggleEl = this.toggleEl();
1237
1238             if(!toggleEl){
1239                 return;
1240             }
1241
1242             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1243         }
1244         
1245     },
1246     
1247     collapse : function()
1248     {
1249         if(this.fireEvent('collapse', this)) {
1250             
1251             this.expanded = false;
1252             
1253             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1254             this.el.select('.panel-body',true).first().addClass('hide');
1255         
1256             var toggleEl = this.toggleEl();
1257
1258             if(!toggleEl){
1259                 return;
1260             }
1261
1262             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1263         }
1264     },
1265     
1266     toggleEl : function()
1267     {
1268         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1269             return;
1270         }
1271         
1272         return this.el.select('.panel-heading .fa',true).first();
1273     },
1274     
1275     headerEl : function()
1276     {
1277         if(!this.el || !this.panel.length || !this.header.length){
1278             return;
1279         }
1280         
1281         return this.el.select('.panel-heading',true).first()
1282     },
1283     
1284     titleEl : function()
1285     {
1286         if(!this.el || !this.panel.length || !this.header.length){
1287             return;
1288         }
1289         
1290         return this.el.select('.panel-title',true).first();
1291     },
1292     
1293     setTitle : function(v)
1294     {
1295         var titleEl = this.titleEl();
1296         
1297         if(!titleEl){
1298             return;
1299         }
1300         
1301         titleEl.dom.innerHTML = v;
1302     },
1303     
1304     getTitle : function()
1305     {
1306         
1307         var titleEl = this.titleEl();
1308         
1309         if(!titleEl){
1310             return '';
1311         }
1312         
1313         return titleEl.dom.innerHTML;
1314     },
1315     
1316     setRightTitle : function(v)
1317     {
1318         var t = this.el.select('.panel-header-right',true).first();
1319         
1320         if(!t){
1321             return;
1322         }
1323         
1324         t.dom.innerHTML = v;
1325     },
1326     
1327     onClick : function(e)
1328     {
1329         e.preventDefault();
1330         
1331         this.fireEvent('click', this, e);
1332     }
1333    
1334 });
1335
1336  /*
1337  * - LGPL
1338  *
1339  * image
1340  * 
1341  */
1342
1343
1344 /**
1345  * @class Roo.bootstrap.Img
1346  * @extends Roo.bootstrap.Component
1347  * Bootstrap Img class
1348  * @cfg {Boolean} imgResponsive false | true
1349  * @cfg {String} border rounded | circle | thumbnail
1350  * @cfg {String} src image source
1351  * @cfg {String} alt image alternative text
1352  * @cfg {String} href a tag href
1353  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1354  * @cfg {String} xsUrl xs image source
1355  * @cfg {String} smUrl sm image source
1356  * @cfg {String} mdUrl md image source
1357  * @cfg {String} lgUrl lg image source
1358  * 
1359  * @constructor
1360  * Create a new Input
1361  * @param {Object} config The config object
1362  */
1363
1364 Roo.bootstrap.Img = function(config){
1365     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1366     
1367     this.addEvents({
1368         // img events
1369         /**
1370          * @event click
1371          * The img click event for the img.
1372          * @param {Roo.EventObject} e
1373          */
1374         "click" : true
1375     });
1376 };
1377
1378 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1379     
1380     imgResponsive: true,
1381     border: '',
1382     src: 'about:blank',
1383     href: false,
1384     target: false,
1385     xsUrl: '',
1386     smUrl: '',
1387     mdUrl: '',
1388     lgUrl: '',
1389
1390     getAutoCreate : function()
1391     {   
1392         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1393             return this.createSingleImg();
1394         }
1395         
1396         var cfg = {
1397             tag: 'div',
1398             cls: 'roo-image-responsive-group',
1399             cn: []
1400         };
1401         var _this = this;
1402         
1403         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1404             
1405             if(!_this[size + 'Url']){
1406                 return;
1407             }
1408             
1409             var img = {
1410                 tag: 'img',
1411                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1412                 html: _this.html || cfg.html,
1413                 src: _this[size + 'Url']
1414             };
1415             
1416             img.cls += ' roo-image-responsive-' + size;
1417             
1418             var s = ['xs', 'sm', 'md', 'lg'];
1419             
1420             s.splice(s.indexOf(size), 1);
1421             
1422             Roo.each(s, function(ss){
1423                 img.cls += ' hidden-' + ss;
1424             });
1425             
1426             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1427                 cfg.cls += ' img-' + _this.border;
1428             }
1429             
1430             if(_this.alt){
1431                 cfg.alt = _this.alt;
1432             }
1433             
1434             if(_this.href){
1435                 var a = {
1436                     tag: 'a',
1437                     href: _this.href,
1438                     cn: [
1439                         img
1440                     ]
1441                 };
1442
1443                 if(this.target){
1444                     a.target = _this.target;
1445                 }
1446             }
1447             
1448             cfg.cn.push((_this.href) ? a : img);
1449             
1450         });
1451         
1452         return cfg;
1453     },
1454     
1455     createSingleImg : function()
1456     {
1457         var cfg = {
1458             tag: 'img',
1459             cls: (this.imgResponsive) ? 'img-responsive' : '',
1460             html : null,
1461             src : 'about:blank'  // just incase src get's set to undefined?!?
1462         };
1463         
1464         cfg.html = this.html || cfg.html;
1465         
1466         cfg.src = this.src || cfg.src;
1467         
1468         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1469             cfg.cls += ' img-' + this.border;
1470         }
1471         
1472         if(this.alt){
1473             cfg.alt = this.alt;
1474         }
1475         
1476         if(this.href){
1477             var a = {
1478                 tag: 'a',
1479                 href: this.href,
1480                 cn: [
1481                     cfg
1482                 ]
1483             };
1484             
1485             if(this.target){
1486                 a.target = this.target;
1487             }
1488             
1489         }
1490         
1491         return (this.href) ? a : cfg;
1492     },
1493     
1494     initEvents: function() 
1495     {
1496         if(!this.href){
1497             this.el.on('click', this.onClick, this);
1498         }
1499         
1500     },
1501     
1502     onClick : function(e)
1503     {
1504         Roo.log('img onclick');
1505         this.fireEvent('click', this, e);
1506     }
1507    
1508 });
1509
1510  /*
1511  * - LGPL
1512  *
1513  * image
1514  * 
1515  */
1516
1517
1518 /**
1519  * @class Roo.bootstrap.Link
1520  * @extends Roo.bootstrap.Component
1521  * Bootstrap Link Class
1522  * @cfg {String} alt image alternative text
1523  * @cfg {String} href a tag href
1524  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1525  * @cfg {String} html the content of the link.
1526  * @cfg {String} anchor name for the anchor link
1527
1528  * @cfg {Boolean} preventDefault (true | false) default false
1529
1530  * 
1531  * @constructor
1532  * Create a new Input
1533  * @param {Object} config The config object
1534  */
1535
1536 Roo.bootstrap.Link = function(config){
1537     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1538     
1539     this.addEvents({
1540         // img events
1541         /**
1542          * @event click
1543          * The img click event for the img.
1544          * @param {Roo.EventObject} e
1545          */
1546         "click" : true
1547     });
1548 };
1549
1550 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1551     
1552     href: false,
1553     target: false,
1554     preventDefault: false,
1555     anchor : false,
1556     alt : false,
1557
1558     getAutoCreate : function()
1559     {
1560         
1561         var cfg = {
1562             tag: 'a'
1563         };
1564         // anchor's do not require html/href...
1565         if (this.anchor === false) {
1566             cfg.html = this.html || '';
1567             cfg.href = this.href || '#';
1568         } else {
1569             cfg.name = this.anchor;
1570             if (this.html !== false) {
1571                 cfg.html = this.html;
1572             }
1573             if (this.href !== false) {
1574                 cfg.href = this.href;
1575             }
1576         }
1577         
1578         if(this.alt !== false){
1579             cfg.alt = this.alt;
1580         }
1581         
1582         
1583         if(this.target !== false) {
1584             cfg.target = this.target;
1585         }
1586         
1587         return cfg;
1588     },
1589     
1590     initEvents: function() {
1591         
1592         if(!this.href || this.preventDefault){
1593             this.el.on('click', this.onClick, this);
1594         }
1595     },
1596     
1597     onClick : function(e)
1598     {
1599         if(this.preventDefault){
1600             e.preventDefault();
1601         }
1602         //Roo.log('img onclick');
1603         this.fireEvent('click', this, e);
1604     }
1605    
1606 });
1607
1608  /*
1609  * - LGPL
1610  *
1611  * header
1612  * 
1613  */
1614
1615 /**
1616  * @class Roo.bootstrap.Header
1617  * @extends Roo.bootstrap.Component
1618  * Bootstrap Header class
1619  * @cfg {String} html content of header
1620  * @cfg {Number} level (1|2|3|4|5|6) default 1
1621  * 
1622  * @constructor
1623  * Create a new Header
1624  * @param {Object} config The config object
1625  */
1626
1627
1628 Roo.bootstrap.Header  = function(config){
1629     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1630 };
1631
1632 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1633     
1634     //href : false,
1635     html : false,
1636     level : 1,
1637     
1638     
1639     
1640     getAutoCreate : function(){
1641         
1642         
1643         
1644         var cfg = {
1645             tag: 'h' + (1 *this.level),
1646             html: this.html || ''
1647         } ;
1648         
1649         return cfg;
1650     }
1651    
1652 });
1653
1654  
1655
1656  /*
1657  * Based on:
1658  * Ext JS Library 1.1.1
1659  * Copyright(c) 2006-2007, Ext JS, LLC.
1660  *
1661  * Originally Released Under LGPL - original licence link has changed is not relivant.
1662  *
1663  * Fork - LGPL
1664  * <script type="text/javascript">
1665  */
1666  
1667 /**
1668  * @class Roo.bootstrap.MenuMgr
1669  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1670  * @singleton
1671  */
1672 Roo.bootstrap.MenuMgr = function(){
1673    var menus, active, groups = {}, attached = false, lastShow = new Date();
1674
1675    // private - called when first menu is created
1676    function init(){
1677        menus = {};
1678        active = new Roo.util.MixedCollection();
1679        Roo.get(document).addKeyListener(27, function(){
1680            if(active.length > 0){
1681                hideAll();
1682            }
1683        });
1684    }
1685
1686    // private
1687    function hideAll(){
1688        if(active && active.length > 0){
1689            var c = active.clone();
1690            c.each(function(m){
1691                m.hide();
1692            });
1693        }
1694    }
1695
1696    // private
1697    function onHide(m){
1698        active.remove(m);
1699        if(active.length < 1){
1700            Roo.get(document).un("mouseup", onMouseDown);
1701             
1702            attached = false;
1703        }
1704    }
1705
1706    // private
1707    function onShow(m){
1708        var last = active.last();
1709        lastShow = new Date();
1710        active.add(m);
1711        if(!attached){
1712           Roo.get(document).on("mouseup", onMouseDown);
1713            
1714            attached = true;
1715        }
1716        if(m.parentMenu){
1717           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1718           m.parentMenu.activeChild = m;
1719        }else if(last && last.isVisible()){
1720           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1721        }
1722    }
1723
1724    // private
1725    function onBeforeHide(m){
1726        if(m.activeChild){
1727            m.activeChild.hide();
1728        }
1729        if(m.autoHideTimer){
1730            clearTimeout(m.autoHideTimer);
1731            delete m.autoHideTimer;
1732        }
1733    }
1734
1735    // private
1736    function onBeforeShow(m){
1737        var pm = m.parentMenu;
1738        if(!pm && !m.allowOtherMenus){
1739            hideAll();
1740        }else if(pm && pm.activeChild && active != m){
1741            pm.activeChild.hide();
1742        }
1743    }
1744
1745    // private this should really trigger on mouseup..
1746    function onMouseDown(e){
1747         Roo.log("on Mouse Up");
1748         
1749         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1750             Roo.log("MenuManager hideAll");
1751             hideAll();
1752             e.stopEvent();
1753         }
1754         
1755         
1756    }
1757
1758    // private
1759    function onBeforeCheck(mi, state){
1760        if(state){
1761            var g = groups[mi.group];
1762            for(var i = 0, l = g.length; i < l; i++){
1763                if(g[i] != mi){
1764                    g[i].setChecked(false);
1765                }
1766            }
1767        }
1768    }
1769
1770    return {
1771
1772        /**
1773         * Hides all menus that are currently visible
1774         */
1775        hideAll : function(){
1776             hideAll();  
1777        },
1778
1779        // private
1780        register : function(menu){
1781            if(!menus){
1782                init();
1783            }
1784            menus[menu.id] = menu;
1785            menu.on("beforehide", onBeforeHide);
1786            menu.on("hide", onHide);
1787            menu.on("beforeshow", onBeforeShow);
1788            menu.on("show", onShow);
1789            var g = menu.group;
1790            if(g && menu.events["checkchange"]){
1791                if(!groups[g]){
1792                    groups[g] = [];
1793                }
1794                groups[g].push(menu);
1795                menu.on("checkchange", onCheck);
1796            }
1797        },
1798
1799         /**
1800          * Returns a {@link Roo.menu.Menu} object
1801          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1802          * be used to generate and return a new Menu instance.
1803          */
1804        get : function(menu){
1805            if(typeof menu == "string"){ // menu id
1806                return menus[menu];
1807            }else if(menu.events){  // menu instance
1808                return menu;
1809            }
1810            /*else if(typeof menu.length == 'number'){ // array of menu items?
1811                return new Roo.bootstrap.Menu({items:menu});
1812            }else{ // otherwise, must be a config
1813                return new Roo.bootstrap.Menu(menu);
1814            }
1815            */
1816            return false;
1817        },
1818
1819        // private
1820        unregister : function(menu){
1821            delete menus[menu.id];
1822            menu.un("beforehide", onBeforeHide);
1823            menu.un("hide", onHide);
1824            menu.un("beforeshow", onBeforeShow);
1825            menu.un("show", onShow);
1826            var g = menu.group;
1827            if(g && menu.events["checkchange"]){
1828                groups[g].remove(menu);
1829                menu.un("checkchange", onCheck);
1830            }
1831        },
1832
1833        // private
1834        registerCheckable : function(menuItem){
1835            var g = menuItem.group;
1836            if(g){
1837                if(!groups[g]){
1838                    groups[g] = [];
1839                }
1840                groups[g].push(menuItem);
1841                menuItem.on("beforecheckchange", onBeforeCheck);
1842            }
1843        },
1844
1845        // private
1846        unregisterCheckable : function(menuItem){
1847            var g = menuItem.group;
1848            if(g){
1849                groups[g].remove(menuItem);
1850                menuItem.un("beforecheckchange", onBeforeCheck);
1851            }
1852        }
1853    };
1854 }();/*
1855  * - LGPL
1856  *
1857  * menu
1858  * 
1859  */
1860
1861 /**
1862  * @class Roo.bootstrap.Menu
1863  * @extends Roo.bootstrap.Component
1864  * Bootstrap Menu class - container for MenuItems
1865  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1866  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1867  * 
1868  * @constructor
1869  * Create a new Menu
1870  * @param {Object} config The config object
1871  */
1872
1873
1874 Roo.bootstrap.Menu = function(config){
1875     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1876     if (this.registerMenu && this.type != 'treeview')  {
1877         Roo.bootstrap.MenuMgr.register(this);
1878     }
1879     this.addEvents({
1880         /**
1881          * @event beforeshow
1882          * Fires before this menu is displayed
1883          * @param {Roo.menu.Menu} this
1884          */
1885         beforeshow : true,
1886         /**
1887          * @event beforehide
1888          * Fires before this menu is hidden
1889          * @param {Roo.menu.Menu} this
1890          */
1891         beforehide : true,
1892         /**
1893          * @event show
1894          * Fires after this menu is displayed
1895          * @param {Roo.menu.Menu} this
1896          */
1897         show : true,
1898         /**
1899          * @event hide
1900          * Fires after this menu is hidden
1901          * @param {Roo.menu.Menu} this
1902          */
1903         hide : true,
1904         /**
1905          * @event click
1906          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1907          * @param {Roo.menu.Menu} this
1908          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1909          * @param {Roo.EventObject} e
1910          */
1911         click : true,
1912         /**
1913          * @event mouseover
1914          * Fires when the mouse is hovering over this menu
1915          * @param {Roo.menu.Menu} this
1916          * @param {Roo.EventObject} e
1917          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1918          */
1919         mouseover : true,
1920         /**
1921          * @event mouseout
1922          * Fires when the mouse exits this menu
1923          * @param {Roo.menu.Menu} this
1924          * @param {Roo.EventObject} e
1925          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1926          */
1927         mouseout : true,
1928         /**
1929          * @event itemclick
1930          * Fires when a menu item contained in this menu is clicked
1931          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1932          * @param {Roo.EventObject} e
1933          */
1934         itemclick: true
1935     });
1936     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1937 };
1938
1939 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1940     
1941    /// html : false,
1942     //align : '',
1943     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1944     type: false,
1945     /**
1946      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1947      */
1948     registerMenu : true,
1949     
1950     menuItems :false, // stores the menu items..
1951     
1952     hidden:true,
1953     
1954         
1955     parentMenu : false,
1956     
1957     getChildContainer : function() {
1958         return this.el;  
1959     },
1960     
1961     getAutoCreate : function(){
1962          
1963         //if (['right'].indexOf(this.align)!==-1) {
1964         //    cfg.cn[1].cls += ' pull-right'
1965         //}
1966         
1967         
1968         var cfg = {
1969             tag : 'ul',
1970             cls : 'dropdown-menu' ,
1971             style : 'z-index:1000'
1972             
1973         };
1974         
1975         if (this.type === 'submenu') {
1976             cfg.cls = 'submenu active';
1977         }
1978         if (this.type === 'treeview') {
1979             cfg.cls = 'treeview-menu';
1980         }
1981         
1982         return cfg;
1983     },
1984     initEvents : function() {
1985         
1986        // Roo.log("ADD event");
1987        // Roo.log(this.triggerEl.dom);
1988         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
1989         
1990         this.triggerEl.addClass('dropdown-toggle');
1991         
1992         
1993         if (Roo.isTouch) {
1994             this.el.on('touchstart'  , this.onTouch, this);
1995         }
1996         this.el.on('click' , this.onClick, this);
1997
1998         this.el.on("mouseover", this.onMouseOver, this);
1999         this.el.on("mouseout", this.onMouseOut, this);
2000         
2001     },
2002     
2003     findTargetItem : function(e)
2004     {
2005         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2006         if(!t){
2007             return false;
2008         }
2009         //Roo.log(t);         Roo.log(t.id);
2010         if(t && t.id){
2011             //Roo.log(this.menuitems);
2012             return this.menuitems.get(t.id);
2013             
2014             //return this.items.get(t.menuItemId);
2015         }
2016         
2017         return false;
2018     },
2019     
2020     onTouch : function(e) 
2021     {
2022         //e.stopEvent(); this make the user popdown broken
2023         this.onClick(e);
2024     },
2025     
2026     onClick : function(e)
2027     {
2028         Roo.log("menu.onClick");
2029         var t = this.findTargetItem(e);
2030         if(!t || t.isContainer){
2031             return;
2032         }
2033         Roo.log(e);
2034         /*
2035         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2036             if(t == this.activeItem && t.shouldDeactivate(e)){
2037                 this.activeItem.deactivate();
2038                 delete this.activeItem;
2039                 return;
2040             }
2041             if(t.canActivate){
2042                 this.setActiveItem(t, true);
2043             }
2044             return;
2045             
2046             
2047         }
2048         */
2049        
2050         Roo.log('pass click event');
2051         
2052         t.onClick(e);
2053         
2054         this.fireEvent("click", this, t, e);
2055         
2056         this.hide();
2057     },
2058      onMouseOver : function(e){
2059         var t  = this.findTargetItem(e);
2060         //Roo.log(t);
2061         //if(t){
2062         //    if(t.canActivate && !t.disabled){
2063         //        this.setActiveItem(t, true);
2064         //    }
2065         //}
2066         
2067         this.fireEvent("mouseover", this, e, t);
2068     },
2069     isVisible : function(){
2070         return !this.hidden;
2071     },
2072      onMouseOut : function(e){
2073         var t  = this.findTargetItem(e);
2074         
2075         //if(t ){
2076         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2077         //        this.activeItem.deactivate();
2078         //        delete this.activeItem;
2079         //    }
2080         //}
2081         this.fireEvent("mouseout", this, e, t);
2082     },
2083     
2084     
2085     /**
2086      * Displays this menu relative to another element
2087      * @param {String/HTMLElement/Roo.Element} element The element to align to
2088      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2089      * the element (defaults to this.defaultAlign)
2090      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2091      */
2092     show : function(el, pos, parentMenu){
2093         this.parentMenu = parentMenu;
2094         if(!this.el){
2095             this.render();
2096         }
2097         this.fireEvent("beforeshow", this);
2098         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2099     },
2100      /**
2101      * Displays this menu at a specific xy position
2102      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2103      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2104      */
2105     showAt : function(xy, parentMenu, /* private: */_e){
2106         this.parentMenu = parentMenu;
2107         if(!this.el){
2108             this.render();
2109         }
2110         if(_e !== false){
2111             this.fireEvent("beforeshow", this);
2112             //xy = this.el.adjustForConstraints(xy);
2113         }
2114         
2115         //this.el.show();
2116         this.hideMenuItems();
2117         this.hidden = false;
2118         this.triggerEl.addClass('open');
2119         
2120         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2121             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2122         }
2123         
2124         this.el.setXY(xy);
2125         this.focus();
2126         this.fireEvent("show", this);
2127     },
2128     
2129     focus : function(){
2130         return;
2131         if(!this.hidden){
2132             this.doFocus.defer(50, this);
2133         }
2134     },
2135
2136     doFocus : function(){
2137         if(!this.hidden){
2138             this.focusEl.focus();
2139         }
2140     },
2141
2142     /**
2143      * Hides this menu and optionally all parent menus
2144      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2145      */
2146     hide : function(deep)
2147     {
2148         
2149         this.hideMenuItems();
2150         if(this.el && this.isVisible()){
2151             this.fireEvent("beforehide", this);
2152             if(this.activeItem){
2153                 this.activeItem.deactivate();
2154                 this.activeItem = null;
2155             }
2156             this.triggerEl.removeClass('open');;
2157             this.hidden = true;
2158             this.fireEvent("hide", this);
2159         }
2160         if(deep === true && this.parentMenu){
2161             this.parentMenu.hide(true);
2162         }
2163     },
2164     
2165     onTriggerPress  : function(e)
2166     {
2167         
2168         Roo.log('trigger press');
2169         //Roo.log(e.getTarget());
2170        // Roo.log(this.triggerEl.dom);
2171        
2172         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2173         var pel = Roo.get(e.getTarget());
2174         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2175            
2176             return;
2177         }
2178         
2179         if (this.isVisible()) {
2180             Roo.log('hide');
2181             this.hide();
2182         } else {
2183             Roo.log('show');
2184             this.show(this.triggerEl, false, false);
2185         }
2186         
2187         e.stopEvent();
2188     },
2189     
2190          
2191        
2192     
2193     hideMenuItems : function()
2194     {
2195         Roo.log("hide Menu Items");
2196         if (!this.el) { 
2197             return;
2198         }
2199         //$(backdrop).remove()
2200         this.el.select('.open',true).each(function(aa) {
2201             
2202             aa.removeClass('open');
2203           //var parent = getParent($(this))
2204           //var relatedTarget = { relatedTarget: this }
2205           
2206            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2207           //if (e.isDefaultPrevented()) return
2208            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2209         });
2210     },
2211     addxtypeChild : function (tree, cntr) {
2212         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2213           
2214         this.menuitems.add(comp);
2215         return comp;
2216
2217     },
2218     getEl : function()
2219     {
2220         Roo.log(this.el);
2221         return this.el;
2222     }
2223 });
2224
2225  
2226  /*
2227  * - LGPL
2228  *
2229  * menu item
2230  * 
2231  */
2232
2233
2234 /**
2235  * @class Roo.bootstrap.MenuItem
2236  * @extends Roo.bootstrap.Component
2237  * Bootstrap MenuItem class
2238  * @cfg {String} html the menu label
2239  * @cfg {String} href the link
2240  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2241  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2242  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2243  * @cfg {String} fa favicon to show on left of menu item.
2244  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2245  * 
2246  * 
2247  * @constructor
2248  * Create a new MenuItem
2249  * @param {Object} config The config object
2250  */
2251
2252
2253 Roo.bootstrap.MenuItem = function(config){
2254     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2255     this.addEvents({
2256         // raw events
2257         /**
2258          * @event click
2259          * The raw click event for the entire grid.
2260          * @param {Roo.bootstrap.MenuItem} this
2261          * @param {Roo.EventObject} e
2262          */
2263         "click" : true
2264     });
2265 };
2266
2267 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2268     
2269     href : false,
2270     html : false,
2271     preventDefault: true,
2272     isContainer : false,
2273     active : false,
2274     fa: false,
2275     
2276     getAutoCreate : function(){
2277         
2278         if(this.isContainer){
2279             return {
2280                 tag: 'li',
2281                 cls: 'dropdown-menu-item'
2282             };
2283         }
2284         var ctag = {
2285             tag: 'span',
2286             html: 'Link'
2287         };
2288         
2289         var anc = {
2290             tag : 'a',
2291             href : '#',
2292             cn : [  ]
2293         };
2294         
2295         if (this.fa !== false) {
2296             anc.cn.push({
2297                 tag : 'i',
2298                 cls : 'fa fa-' + this.fa
2299             });
2300         }
2301         
2302         anc.cn.push(ctag);
2303         
2304         
2305         var cfg= {
2306             tag: 'li',
2307             cls: 'dropdown-menu-item',
2308             cn: [ anc ]
2309         };
2310         if (this.parent().type == 'treeview') {
2311             cfg.cls = 'treeview-menu';
2312         }
2313         if (this.active) {
2314             cfg.cls += ' active';
2315         }
2316         
2317         
2318         
2319         anc.href = this.href || cfg.cn[0].href ;
2320         ctag.html = this.html || cfg.cn[0].html ;
2321         return cfg;
2322     },
2323     
2324     initEvents: function()
2325     {
2326         if (this.parent().type == 'treeview') {
2327             this.el.select('a').on('click', this.onClick, this);
2328         }
2329         if (this.menu) {
2330             this.menu.parentType = this.xtype;
2331             this.menu.triggerEl = this.el;
2332             this.menu = this.addxtype(Roo.apply({}, this.menu));
2333         }
2334         
2335     },
2336     onClick : function(e)
2337     {
2338         Roo.log('item on click ');
2339         //if(this.preventDefault){
2340         //    e.preventDefault();
2341         //}
2342         //this.parent().hideMenuItems();
2343         
2344         this.fireEvent('click', this, e);
2345     },
2346     getEl : function()
2347     {
2348         return this.el;
2349     }
2350 });
2351
2352  
2353
2354  /*
2355  * - LGPL
2356  *
2357  * menu separator
2358  * 
2359  */
2360
2361
2362 /**
2363  * @class Roo.bootstrap.MenuSeparator
2364  * @extends Roo.bootstrap.Component
2365  * Bootstrap MenuSeparator class
2366  * 
2367  * @constructor
2368  * Create a new MenuItem
2369  * @param {Object} config The config object
2370  */
2371
2372
2373 Roo.bootstrap.MenuSeparator = function(config){
2374     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2375 };
2376
2377 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2378     
2379     getAutoCreate : function(){
2380         var cfg = {
2381             cls: 'divider',
2382             tag : 'li'
2383         };
2384         
2385         return cfg;
2386     }
2387    
2388 });
2389
2390  
2391
2392  
2393 /*
2394 * Licence: LGPL
2395 */
2396
2397 /**
2398  * @class Roo.bootstrap.Modal
2399  * @extends Roo.bootstrap.Component
2400  * Bootstrap Modal class
2401  * @cfg {String} title Title of dialog
2402  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2403  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2404  * @cfg {Boolean} specificTitle default false
2405  * @cfg {Array} buttons Array of buttons or standard button set..
2406  * @cfg {String} buttonPosition (left|right|center) default right
2407  * @cfg {Boolean} animate default true
2408  * @cfg {Boolean} allow_close default true
2409  * 
2410  * @constructor
2411  * Create a new Modal Dialog
2412  * @param {Object} config The config object
2413  */
2414
2415 Roo.bootstrap.Modal = function(config){
2416     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2417     this.addEvents({
2418         // raw events
2419         /**
2420          * @event btnclick
2421          * The raw btnclick event for the button
2422          * @param {Roo.EventObject} e
2423          */
2424         "btnclick" : true
2425     });
2426     this.buttons = this.buttons || [];
2427      
2428     if (this.tmpl) {
2429         this.tmpl = Roo.factory(this.tmpl);
2430     }
2431     
2432 };
2433
2434 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2435     
2436     title : 'test dialog',
2437    
2438     buttons : false,
2439     
2440     // set on load...
2441      
2442     html: false,
2443     
2444     tmp: false,
2445     
2446     specificTitle: false,
2447     
2448     buttonPosition: 'right',
2449     
2450     allow_close : true,
2451     
2452     animate : true,
2453     
2454     
2455      // private
2456     bodyEl:  false,
2457     footerEl:  false,
2458     titleEl:  false,
2459     closeEl:  false,
2460     
2461     
2462     onRender : function(ct, position)
2463     {
2464         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2465      
2466         if(!this.el){
2467             var cfg = Roo.apply({},  this.getAutoCreate());
2468             cfg.id = Roo.id();
2469             //if(!cfg.name){
2470             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2471             //}
2472             //if (!cfg.name.length) {
2473             //    delete cfg.name;
2474            // }
2475             if (this.cls) {
2476                 cfg.cls += ' ' + this.cls;
2477             }
2478             if (this.style) {
2479                 cfg.style = this.style;
2480             }
2481             this.el = Roo.get(document.body).createChild(cfg, position);
2482         }
2483         //var type = this.el.dom.type;
2484         
2485         
2486         if(this.tabIndex !== undefined){
2487             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2488         }
2489         
2490         
2491         this.bodyEl = this.el.select('.modal-body',true).first();
2492         this.closeEl = this.el.select('.modal-header .close', true).first();
2493         this.footerEl = this.el.select('.modal-footer',true).first();
2494         this.titleEl = this.el.select('.modal-title',true).first();
2495         
2496         
2497          
2498         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2499         this.maskEl.enableDisplayMode("block");
2500         this.maskEl.hide();
2501         //this.el.addClass("x-dlg-modal");
2502     
2503         if (this.buttons.length) {
2504             Roo.each(this.buttons, function(bb) {
2505                 var b = Roo.apply({}, bb);
2506                 b.xns = b.xns || Roo.bootstrap;
2507                 b.xtype = b.xtype || 'Button';
2508                 if (typeof(b.listeners) == 'undefined') {
2509                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2510                 }
2511                 
2512                 var btn = Roo.factory(b);
2513                 
2514                 btn.onRender(this.el.select('.modal-footer div').first());
2515                 
2516             },this);
2517         }
2518         // render the children.
2519         var nitems = [];
2520         
2521         if(typeof(this.items) != 'undefined'){
2522             var items = this.items;
2523             delete this.items;
2524
2525             for(var i =0;i < items.length;i++) {
2526                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2527             }
2528         }
2529         
2530         this.items = nitems;
2531         
2532         // where are these used - they used to be body/close/footer
2533         
2534        
2535         this.initEvents();
2536         //this.el.addClass([this.fieldClass, this.cls]);
2537         
2538     },
2539     
2540     getAutoCreate : function(){
2541         
2542         
2543         var bdy = {
2544                 cls : 'modal-body',
2545                 html : this.html || ''
2546         };
2547         
2548         var title = {
2549             tag: 'h4',
2550             cls : 'modal-title',
2551             html : this.title
2552         };
2553         
2554         if(this.specificTitle){
2555             title = this.title;
2556             
2557         };
2558         
2559         var header = [];
2560         if (this.allow_close) {
2561             header.push({
2562                 tag: 'button',
2563                 cls : 'close',
2564                 html : '&times'
2565             });
2566         }
2567         header.push(title);
2568         
2569         var modal = {
2570             cls: "modal",
2571             style : 'display: none',
2572             cn : [
2573                 {
2574                     cls: "modal-dialog",
2575                     cn : [
2576                         {
2577                             cls : "modal-content",
2578                             cn : [
2579                                 {
2580                                     cls : 'modal-header',
2581                                     cn : header
2582                                 },
2583                                 bdy,
2584                                 {
2585                                     cls : 'modal-footer',
2586                                     cn : [
2587                                         {
2588                                             tag: 'div',
2589                                             cls: 'btn-' + this.buttonPosition
2590                                         }
2591                                     ]
2592                                     
2593                                 }
2594                                 
2595                                 
2596                             ]
2597                             
2598                         }
2599                     ]
2600                         
2601                 }
2602             ]
2603         };
2604         
2605         if(this.animate){
2606             modal.cls += ' fade';
2607         }
2608         
2609         return modal;
2610           
2611     },
2612     getChildContainer : function() {
2613          
2614          return this.bodyEl;
2615         
2616     },
2617     getButtonContainer : function() {
2618          return this.el.select('.modal-footer div',true).first();
2619         
2620     },
2621     initEvents : function()
2622     {
2623         if (this.allow_close) {
2624             this.closeEl.on('click', this.hide, this);
2625         }
2626         
2627         var _this = this;
2628         
2629         window.addEventListener("resize", function() { _this.resize(); } );
2630
2631     },
2632     
2633     resize : function()
2634     {
2635         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2636     },
2637     
2638     show : function() {
2639         
2640         if (!this.rendered) {
2641             this.render();
2642         }
2643         
2644         this.el.setStyle('display', 'block');
2645         
2646         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2647             var _this = this;
2648             (function(){
2649                 this.el.addClass('in');
2650             }).defer(50, this);
2651         }else{
2652             this.el.addClass('in');
2653             
2654         }
2655         
2656         // not sure how we can show data in here.. 
2657         //if (this.tmpl) {
2658         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2659         //}
2660         
2661         Roo.get(document.body).addClass("x-body-masked");
2662         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2663         this.maskEl.show();
2664         this.el.setStyle('zIndex', '10001');
2665        
2666         this.fireEvent('show', this);
2667          
2668         
2669         
2670     },
2671     hide : function()
2672     {
2673         this.maskEl.hide();
2674         Roo.get(document.body).removeClass("x-body-masked");
2675         this.el.removeClass('in');
2676         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2677         
2678         if(this.animate){ // why
2679             var _this = this;
2680             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2681         }else{
2682             this.el.setStyle('display', 'none');
2683         }
2684         
2685         this.fireEvent('hide', this);
2686     },
2687     
2688     addButton : function(str, cb)
2689     {
2690          
2691         
2692         var b = Roo.apply({}, { html : str } );
2693         b.xns = b.xns || Roo.bootstrap;
2694         b.xtype = b.xtype || 'Button';
2695         if (typeof(b.listeners) == 'undefined') {
2696             b.listeners = { click : cb.createDelegate(this)  };
2697         }
2698         
2699         var btn = Roo.factory(b);
2700            
2701         btn.onRender(this.el.select('.modal-footer div').first());
2702         
2703         return btn;   
2704        
2705     },
2706     
2707     setDefaultButton : function(btn)
2708     {
2709         //this.el.select('.modal-footer').()
2710     },
2711     diff : false,
2712     
2713     resizeTo: function(w,h)
2714     {
2715         // skip.. ?? why??
2716         
2717         this.el.select('.modal-dialog',true).first().setWidth(w);
2718         if (this.diff === false) {
2719             this.diff = this.el.select('.modal-dialog',true).first().getHeight() - this.el.select('.modal-body',true).first().getHeight();
2720         }
2721         
2722         this.el.select('.modal-body',true).first().setHeight(h-this.diff);
2723         
2724         
2725     },
2726     setContentSize  : function(w, h)
2727     {
2728         
2729     },
2730     onButtonClick: function(btn,e)
2731     {
2732         //Roo.log([a,b,c]);
2733         this.fireEvent('btnclick', btn.name, e);
2734     },
2735      /**
2736      * Set the title of the Dialog
2737      * @param {String} str new Title
2738      */
2739     setTitle: function(str) {
2740         this.titleEl.dom.innerHTML = str;    
2741     },
2742     /**
2743      * Set the body of the Dialog
2744      * @param {String} str new Title
2745      */
2746     setBody: function(str) {
2747         this.bodyEl.dom.innerHTML = str;    
2748     },
2749     /**
2750      * Set the body of the Dialog using the template
2751      * @param {Obj} data - apply this data to the template and replace the body contents.
2752      */
2753     applyBody: function(obj)
2754     {
2755         if (!this.tmpl) {
2756             Roo.log("Error - using apply Body without a template");
2757             //code
2758         }
2759         this.tmpl.overwrite(this.bodyEl, obj);
2760     }
2761     
2762 });
2763
2764
2765 Roo.apply(Roo.bootstrap.Modal,  {
2766     /**
2767          * Button config that displays a single OK button
2768          * @type Object
2769          */
2770         OK :  [{
2771             name : 'ok',
2772             weight : 'primary',
2773             html : 'OK'
2774         }], 
2775         /**
2776          * Button config that displays Yes and No buttons
2777          * @type Object
2778          */
2779         YESNO : [
2780             {
2781                 name  : 'no',
2782                 html : 'No'
2783             },
2784             {
2785                 name  :'yes',
2786                 weight : 'primary',
2787                 html : 'Yes'
2788             }
2789         ],
2790         
2791         /**
2792          * Button config that displays OK and Cancel buttons
2793          * @type Object
2794          */
2795         OKCANCEL : [
2796             {
2797                name : 'cancel',
2798                 html : 'Cancel'
2799             },
2800             {
2801                 name : 'ok',
2802                 weight : 'primary',
2803                 html : 'OK'
2804             }
2805         ],
2806         /**
2807          * Button config that displays Yes, No and Cancel buttons
2808          * @type Object
2809          */
2810         YESNOCANCEL : [
2811             {
2812                 name : 'yes',
2813                 weight : 'primary',
2814                 html : 'Yes'
2815             },
2816             {
2817                 name : 'no',
2818                 html : 'No'
2819             },
2820             {
2821                 name : 'cancel',
2822                 html : 'Cancel'
2823             }
2824         ]
2825 });
2826  
2827  /*
2828  * - LGPL
2829  *
2830  * messagebox - can be used as a replace
2831  * 
2832  */
2833 /**
2834  * @class Roo.MessageBox
2835  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2836  * Example usage:
2837  *<pre><code>
2838 // Basic alert:
2839 Roo.Msg.alert('Status', 'Changes saved successfully.');
2840
2841 // Prompt for user data:
2842 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2843     if (btn == 'ok'){
2844         // process text value...
2845     }
2846 });
2847
2848 // Show a dialog using config options:
2849 Roo.Msg.show({
2850    title:'Save Changes?',
2851    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2852    buttons: Roo.Msg.YESNOCANCEL,
2853    fn: processResult,
2854    animEl: 'elId'
2855 });
2856 </code></pre>
2857  * @singleton
2858  */
2859 Roo.bootstrap.MessageBox = function(){
2860     var dlg, opt, mask, waitTimer;
2861     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2862     var buttons, activeTextEl, bwidth;
2863
2864     
2865     // private
2866     var handleButton = function(button){
2867         dlg.hide();
2868         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2869     };
2870
2871     // private
2872     var handleHide = function(){
2873         if(opt && opt.cls){
2874             dlg.el.removeClass(opt.cls);
2875         }
2876         //if(waitTimer){
2877         //    Roo.TaskMgr.stop(waitTimer);
2878         //    waitTimer = null;
2879         //}
2880     };
2881
2882     // private
2883     var updateButtons = function(b){
2884         var width = 0;
2885         if(!b){
2886             buttons["ok"].hide();
2887             buttons["cancel"].hide();
2888             buttons["yes"].hide();
2889             buttons["no"].hide();
2890             //dlg.footer.dom.style.display = 'none';
2891             return width;
2892         }
2893         dlg.footerEl.dom.style.display = '';
2894         for(var k in buttons){
2895             if(typeof buttons[k] != "function"){
2896                 if(b[k]){
2897                     buttons[k].show();
2898                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2899                     width += buttons[k].el.getWidth()+15;
2900                 }else{
2901                     buttons[k].hide();
2902                 }
2903             }
2904         }
2905         return width;
2906     };
2907
2908     // private
2909     var handleEsc = function(d, k, e){
2910         if(opt && opt.closable !== false){
2911             dlg.hide();
2912         }
2913         if(e){
2914             e.stopEvent();
2915         }
2916     };
2917
2918     return {
2919         /**
2920          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2921          * @return {Roo.BasicDialog} The BasicDialog element
2922          */
2923         getDialog : function(){
2924            if(!dlg){
2925                 dlg = new Roo.bootstrap.Modal( {
2926                     //draggable: true,
2927                     //resizable:false,
2928                     //constraintoviewport:false,
2929                     //fixedcenter:true,
2930                     //collapsible : false,
2931                     //shim:true,
2932                     //modal: true,
2933                   //  width:400,
2934                   //  height:100,
2935                     //buttonAlign:"center",
2936                     closeClick : function(){
2937                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2938                             handleButton("no");
2939                         }else{
2940                             handleButton("cancel");
2941                         }
2942                     }
2943                 });
2944                 dlg.render();
2945                 dlg.on("hide", handleHide);
2946                 mask = dlg.mask;
2947                 //dlg.addKeyListener(27, handleEsc);
2948                 buttons = {};
2949                 this.buttons = buttons;
2950                 var bt = this.buttonText;
2951                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2952                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2953                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2954                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2955                 //Roo.log(buttons);
2956                 bodyEl = dlg.bodyEl.createChild({
2957
2958                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2959                         '<textarea class="roo-mb-textarea"></textarea>' +
2960                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
2961                 });
2962                 msgEl = bodyEl.dom.firstChild;
2963                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2964                 textboxEl.enableDisplayMode();
2965                 textboxEl.addKeyListener([10,13], function(){
2966                     if(dlg.isVisible() && opt && opt.buttons){
2967                         if(opt.buttons.ok){
2968                             handleButton("ok");
2969                         }else if(opt.buttons.yes){
2970                             handleButton("yes");
2971                         }
2972                     }
2973                 });
2974                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2975                 textareaEl.enableDisplayMode();
2976                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2977                 progressEl.enableDisplayMode();
2978                 var pf = progressEl.dom.firstChild;
2979                 if (pf) {
2980                     pp = Roo.get(pf.firstChild);
2981                     pp.setHeight(pf.offsetHeight);
2982                 }
2983                 
2984             }
2985             return dlg;
2986         },
2987
2988         /**
2989          * Updates the message box body text
2990          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2991          * the XHTML-compliant non-breaking space character '&amp;#160;')
2992          * @return {Roo.MessageBox} This message box
2993          */
2994         updateText : function(text){
2995             if(!dlg.isVisible() && !opt.width){
2996                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2997             }
2998             msgEl.innerHTML = text || '&#160;';
2999       
3000             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3001             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3002             var w = Math.max(
3003                     Math.min(opt.width || cw , this.maxWidth), 
3004                     Math.max(opt.minWidth || this.minWidth, bwidth)
3005             );
3006             if(opt.prompt){
3007                 activeTextEl.setWidth(w);
3008             }
3009             if(dlg.isVisible()){
3010                 dlg.fixedcenter = false;
3011             }
3012             // to big, make it scroll. = But as usual stupid IE does not support
3013             // !important..
3014             
3015             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3016                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3017                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3018             } else {
3019                 bodyEl.dom.style.height = '';
3020                 bodyEl.dom.style.overflowY = '';
3021             }
3022             if (cw > w) {
3023                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3024             } else {
3025                 bodyEl.dom.style.overflowX = '';
3026             }
3027             
3028             dlg.setContentSize(w, bodyEl.getHeight());
3029             if(dlg.isVisible()){
3030                 dlg.fixedcenter = true;
3031             }
3032             return this;
3033         },
3034
3035         /**
3036          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3037          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3038          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3039          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3040          * @return {Roo.MessageBox} This message box
3041          */
3042         updateProgress : function(value, text){
3043             if(text){
3044                 this.updateText(text);
3045             }
3046             if (pp) { // weird bug on my firefox - for some reason this is not defined
3047                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3048             }
3049             return this;
3050         },        
3051
3052         /**
3053          * Returns true if the message box is currently displayed
3054          * @return {Boolean} True if the message box is visible, else false
3055          */
3056         isVisible : function(){
3057             return dlg && dlg.isVisible();  
3058         },
3059
3060         /**
3061          * Hides the message box if it is displayed
3062          */
3063         hide : function(){
3064             if(this.isVisible()){
3065                 dlg.hide();
3066             }  
3067         },
3068
3069         /**
3070          * Displays a new message box, or reinitializes an existing message box, based on the config options
3071          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3072          * The following config object properties are supported:
3073          * <pre>
3074 Property    Type             Description
3075 ----------  ---------------  ------------------------------------------------------------------------------------
3076 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3077                                    closes (defaults to undefined)
3078 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3079                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3080 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3081                                    progress and wait dialogs will ignore this property and always hide the
3082                                    close button as they can only be closed programmatically.
3083 cls               String           A custom CSS class to apply to the message box element
3084 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3085                                    displayed (defaults to 75)
3086 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3087                                    function will be btn (the name of the button that was clicked, if applicable,
3088                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3089                                    Progress and wait dialogs will ignore this option since they do not respond to
3090                                    user actions and can only be closed programmatically, so any required function
3091                                    should be called by the same code after it closes the dialog.
3092 icon              String           A CSS class that provides a background image to be used as an icon for
3093                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3094 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3095 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3096 modal             Boolean          False to allow user interaction with the page while the message box is
3097                                    displayed (defaults to true)
3098 msg               String           A string that will replace the existing message box body text (defaults
3099                                    to the XHTML-compliant non-breaking space character '&#160;')
3100 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3101 progress          Boolean          True to display a progress bar (defaults to false)
3102 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3103 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3104 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3105 title             String           The title text
3106 value             String           The string value to set into the active textbox element if displayed
3107 wait              Boolean          True to display a progress bar (defaults to false)
3108 width             Number           The width of the dialog in pixels
3109 </pre>
3110          *
3111          * Example usage:
3112          * <pre><code>
3113 Roo.Msg.show({
3114    title: 'Address',
3115    msg: 'Please enter your address:',
3116    width: 300,
3117    buttons: Roo.MessageBox.OKCANCEL,
3118    multiline: true,
3119    fn: saveAddress,
3120    animEl: 'addAddressBtn'
3121 });
3122 </code></pre>
3123          * @param {Object} config Configuration options
3124          * @return {Roo.MessageBox} This message box
3125          */
3126         show : function(options)
3127         {
3128             
3129             // this causes nightmares if you show one dialog after another
3130             // especially on callbacks..
3131              
3132             if(this.isVisible()){
3133                 
3134                 this.hide();
3135                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3136                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3137                 Roo.log("New Dialog Message:" +  options.msg )
3138                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3139                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3140                 
3141             }
3142             var d = this.getDialog();
3143             opt = options;
3144             d.setTitle(opt.title || "&#160;");
3145             d.closeEl.setDisplayed(opt.closable !== false);
3146             activeTextEl = textboxEl;
3147             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3148             if(opt.prompt){
3149                 if(opt.multiline){
3150                     textboxEl.hide();
3151                     textareaEl.show();
3152                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3153                         opt.multiline : this.defaultTextHeight);
3154                     activeTextEl = textareaEl;
3155                 }else{
3156                     textboxEl.show();
3157                     textareaEl.hide();
3158                 }
3159             }else{
3160                 textboxEl.hide();
3161                 textareaEl.hide();
3162             }
3163             progressEl.setDisplayed(opt.progress === true);
3164             this.updateProgress(0);
3165             activeTextEl.dom.value = opt.value || "";
3166             if(opt.prompt){
3167                 dlg.setDefaultButton(activeTextEl);
3168             }else{
3169                 var bs = opt.buttons;
3170                 var db = null;
3171                 if(bs && bs.ok){
3172                     db = buttons["ok"];
3173                 }else if(bs && bs.yes){
3174                     db = buttons["yes"];
3175                 }
3176                 dlg.setDefaultButton(db);
3177             }
3178             bwidth = updateButtons(opt.buttons);
3179             this.updateText(opt.msg);
3180             if(opt.cls){
3181                 d.el.addClass(opt.cls);
3182             }
3183             d.proxyDrag = opt.proxyDrag === true;
3184             d.modal = opt.modal !== false;
3185             d.mask = opt.modal !== false ? mask : false;
3186             if(!d.isVisible()){
3187                 // force it to the end of the z-index stack so it gets a cursor in FF
3188                 document.body.appendChild(dlg.el.dom);
3189                 d.animateTarget = null;
3190                 d.show(options.animEl);
3191             }
3192             return this;
3193         },
3194
3195         /**
3196          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3197          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3198          * and closing the message box when the process is complete.
3199          * @param {String} title The title bar text
3200          * @param {String} msg The message box body text
3201          * @return {Roo.MessageBox} This message box
3202          */
3203         progress : function(title, msg){
3204             this.show({
3205                 title : title,
3206                 msg : msg,
3207                 buttons: false,
3208                 progress:true,
3209                 closable:false,
3210                 minWidth: this.minProgressWidth,
3211                 modal : true
3212             });
3213             return this;
3214         },
3215
3216         /**
3217          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3218          * If a callback function is passed it will be called after the user clicks the button, and the
3219          * id of the button that was clicked will be passed as the only parameter to the callback
3220          * (could also be the top-right close button).
3221          * @param {String} title The title bar text
3222          * @param {String} msg The message box body text
3223          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3224          * @param {Object} scope (optional) The scope of the callback function
3225          * @return {Roo.MessageBox} This message box
3226          */
3227         alert : function(title, msg, fn, scope){
3228             this.show({
3229                 title : title,
3230                 msg : msg,
3231                 buttons: this.OK,
3232                 fn: fn,
3233                 scope : scope,
3234                 modal : true
3235             });
3236             return this;
3237         },
3238
3239         /**
3240          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3241          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3242          * You are responsible for closing the message box when the process is complete.
3243          * @param {String} msg The message box body text
3244          * @param {String} title (optional) The title bar text
3245          * @return {Roo.MessageBox} This message box
3246          */
3247         wait : function(msg, title){
3248             this.show({
3249                 title : title,
3250                 msg : msg,
3251                 buttons: false,
3252                 closable:false,
3253                 progress:true,
3254                 modal:true,
3255                 width:300,
3256                 wait:true
3257             });
3258             waitTimer = Roo.TaskMgr.start({
3259                 run: function(i){
3260                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3261                 },
3262                 interval: 1000
3263             });
3264             return this;
3265         },
3266
3267         /**
3268          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3269          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3270          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3271          * @param {String} title The title bar text
3272          * @param {String} msg The message box body text
3273          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3274          * @param {Object} scope (optional) The scope of the callback function
3275          * @return {Roo.MessageBox} This message box
3276          */
3277         confirm : function(title, msg, fn, scope){
3278             this.show({
3279                 title : title,
3280                 msg : msg,
3281                 buttons: this.YESNO,
3282                 fn: fn,
3283                 scope : scope,
3284                 modal : true
3285             });
3286             return this;
3287         },
3288
3289         /**
3290          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3291          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3292          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3293          * (could also be the top-right close button) and the text that was entered will be passed as the two
3294          * parameters to the callback.
3295          * @param {String} title The title bar text
3296          * @param {String} msg The message box body text
3297          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3298          * @param {Object} scope (optional) The scope of the callback function
3299          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3300          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3301          * @return {Roo.MessageBox} This message box
3302          */
3303         prompt : function(title, msg, fn, scope, multiline){
3304             this.show({
3305                 title : title,
3306                 msg : msg,
3307                 buttons: this.OKCANCEL,
3308                 fn: fn,
3309                 minWidth:250,
3310                 scope : scope,
3311                 prompt:true,
3312                 multiline: multiline,
3313                 modal : true
3314             });
3315             return this;
3316         },
3317
3318         /**
3319          * Button config that displays a single OK button
3320          * @type Object
3321          */
3322         OK : {ok:true},
3323         /**
3324          * Button config that displays Yes and No buttons
3325          * @type Object
3326          */
3327         YESNO : {yes:true, no:true},
3328         /**
3329          * Button config that displays OK and Cancel buttons
3330          * @type Object
3331          */
3332         OKCANCEL : {ok:true, cancel:true},
3333         /**
3334          * Button config that displays Yes, No and Cancel buttons
3335          * @type Object
3336          */
3337         YESNOCANCEL : {yes:true, no:true, cancel:true},
3338
3339         /**
3340          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3341          * @type Number
3342          */
3343         defaultTextHeight : 75,
3344         /**
3345          * The maximum width in pixels of the message box (defaults to 600)
3346          * @type Number
3347          */
3348         maxWidth : 600,
3349         /**
3350          * The minimum width in pixels of the message box (defaults to 100)
3351          * @type Number
3352          */
3353         minWidth : 100,
3354         /**
3355          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3356          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3357          * @type Number
3358          */
3359         minProgressWidth : 250,
3360         /**
3361          * An object containing the default button text strings that can be overriden for localized language support.
3362          * Supported properties are: ok, cancel, yes and no.
3363          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3364          * @type Object
3365          */
3366         buttonText : {
3367             ok : "OK",
3368             cancel : "Cancel",
3369             yes : "Yes",
3370             no : "No"
3371         }
3372     };
3373 }();
3374
3375 /**
3376  * Shorthand for {@link Roo.MessageBox}
3377  */
3378 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3379 Roo.Msg = Roo.Msg || Roo.MessageBox;
3380 /*
3381  * - LGPL
3382  *
3383  * navbar
3384  * 
3385  */
3386
3387 /**
3388  * @class Roo.bootstrap.Navbar
3389  * @extends Roo.bootstrap.Component
3390  * Bootstrap Navbar class
3391
3392  * @constructor
3393  * Create a new Navbar
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Navbar = function(config){
3399     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3400     
3401 };
3402
3403 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3404     
3405     
3406    
3407     // private
3408     navItems : false,
3409     loadMask : false,
3410     
3411     
3412     getAutoCreate : function(){
3413         
3414         
3415         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3416         
3417     },
3418     
3419     initEvents :function ()
3420     {
3421         //Roo.log(this.el.select('.navbar-toggle',true));
3422         this.el.select('.navbar-toggle',true).on('click', function() {
3423            // Roo.log('click');
3424             this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3425         }, this);
3426         
3427         var mark = {
3428             tag: "div",
3429             cls:"x-dlg-mask"
3430         };
3431         
3432         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3433         
3434         var size = this.el.getSize();
3435         this.maskEl.setSize(size.width, size.height);
3436         this.maskEl.enableDisplayMode("block");
3437         this.maskEl.hide();
3438         
3439         if(this.loadMask){
3440             this.maskEl.show();
3441         }
3442     },
3443     
3444     
3445     getChildContainer : function()
3446     {
3447         if (this.el.select('.collapse').getCount()) {
3448             return this.el.select('.collapse',true).first();
3449         }
3450         
3451         return this.el;
3452     },
3453     
3454     mask : function()
3455     {
3456         this.maskEl.show();
3457     },
3458     
3459     unmask : function()
3460     {
3461         this.maskEl.hide();
3462     } 
3463     
3464     
3465     
3466     
3467 });
3468
3469
3470
3471  
3472
3473  /*
3474  * - LGPL
3475  *
3476  * navbar
3477  * 
3478  */
3479
3480 /**
3481  * @class Roo.bootstrap.NavSimplebar
3482  * @extends Roo.bootstrap.Navbar
3483  * Bootstrap Sidebar class
3484  *
3485  * @cfg {Boolean} inverse is inverted color
3486  * 
3487  * @cfg {String} type (nav | pills | tabs)
3488  * @cfg {Boolean} arrangement stacked | justified
3489  * @cfg {String} align (left | right) alignment
3490  * 
3491  * @cfg {Boolean} main (true|false) main nav bar? default false
3492  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3493  * 
3494  * @cfg {String} tag (header|footer|nav|div) default is nav 
3495
3496  * 
3497  * 
3498  * 
3499  * @constructor
3500  * Create a new Sidebar
3501  * @param {Object} config The config object
3502  */
3503
3504
3505 Roo.bootstrap.NavSimplebar = function(config){
3506     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3507 };
3508
3509 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3510     
3511     inverse: false,
3512     
3513     type: false,
3514     arrangement: '',
3515     align : false,
3516     
3517     
3518     
3519     main : false,
3520     
3521     
3522     tag : false,
3523     
3524     
3525     getAutoCreate : function(){
3526         
3527         
3528         var cfg = {
3529             tag : this.tag || 'div',
3530             cls : 'navbar'
3531         };
3532           
3533         
3534         cfg.cn = [
3535             {
3536                 cls: 'nav',
3537                 tag : 'ul'
3538             }
3539         ];
3540         
3541          
3542         this.type = this.type || 'nav';
3543         if (['tabs','pills'].indexOf(this.type)!==-1) {
3544             cfg.cn[0].cls += ' nav-' + this.type
3545         
3546         
3547         } else {
3548             if (this.type!=='nav') {
3549                 Roo.log('nav type must be nav/tabs/pills')
3550             }
3551             cfg.cn[0].cls += ' navbar-nav'
3552         }
3553         
3554         
3555         
3556         
3557         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3558             cfg.cn[0].cls += ' nav-' + this.arrangement;
3559         }
3560         
3561         
3562         if (this.align === 'right') {
3563             cfg.cn[0].cls += ' navbar-right';
3564         }
3565         
3566         if (this.inverse) {
3567             cfg.cls += ' navbar-inverse';
3568             
3569         }
3570         
3571         
3572         return cfg;
3573     
3574         
3575     }
3576     
3577     
3578     
3579 });
3580
3581
3582
3583  
3584
3585  
3586        /*
3587  * - LGPL
3588  *
3589  * navbar
3590  * 
3591  */
3592
3593 /**
3594  * @class Roo.bootstrap.NavHeaderbar
3595  * @extends Roo.bootstrap.NavSimplebar
3596  * Bootstrap Sidebar class
3597  *
3598  * @cfg {String} brand what is brand
3599  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3600  * @cfg {String} brand_href href of the brand
3601  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3602  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3603  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3604  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3605  * 
3606  * @constructor
3607  * Create a new Sidebar
3608  * @param {Object} config The config object
3609  */
3610
3611
3612 Roo.bootstrap.NavHeaderbar = function(config){
3613     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3614       
3615 };
3616
3617 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3618     
3619     position: '',
3620     brand: '',
3621     brand_href: false,
3622     srButton : true,
3623     autohide : false,
3624     desktopCenter : false,
3625    
3626     
3627     getAutoCreate : function(){
3628         
3629         var   cfg = {
3630             tag: this.nav || 'nav',
3631             cls: 'navbar',
3632             role: 'navigation',
3633             cn: []
3634         };
3635         
3636         var cn = cfg.cn;
3637         if (this.desktopCenter) {
3638             cn.push({cls : 'container', cn : []});
3639             cn = cn[0].cn;
3640         }
3641         
3642         if(this.srButton){
3643             cn.push({
3644                 tag: 'div',
3645                 cls: 'navbar-header',
3646                 cn: [
3647                     {
3648                         tag: 'button',
3649                         type: 'button',
3650                         cls: 'navbar-toggle',
3651                         'data-toggle': 'collapse',
3652                         cn: [
3653                             {
3654                                 tag: 'span',
3655                                 cls: 'sr-only',
3656                                 html: 'Toggle navigation'
3657                             },
3658                             {
3659                                 tag: 'span',
3660                                 cls: 'icon-bar'
3661                             },
3662                             {
3663                                 tag: 'span',
3664                                 cls: 'icon-bar'
3665                             },
3666                             {
3667                                 tag: 'span',
3668                                 cls: 'icon-bar'
3669                             }
3670                         ]
3671                     }
3672                 ]
3673             });
3674         }
3675         
3676         cn.push({
3677             tag: 'div',
3678             cls: 'collapse navbar-collapse',
3679             cn : []
3680         });
3681         
3682         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3683         
3684         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3685             cfg.cls += ' navbar-' + this.position;
3686             
3687             // tag can override this..
3688             
3689             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3690         }
3691         
3692         if (this.brand !== '') {
3693             cn[0].cn.push({
3694                 tag: 'a',
3695                 href: this.brand_href ? this.brand_href : '#',
3696                 cls: 'navbar-brand',
3697                 cn: [
3698                 this.brand
3699                 ]
3700             });
3701         }
3702         
3703         if(this.main){
3704             cfg.cls += ' main-nav';
3705         }
3706         
3707         
3708         return cfg;
3709
3710         
3711     },
3712     getHeaderChildContainer : function()
3713     {
3714         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3715             return this.el.select('.navbar-header',true).first();
3716         }
3717         
3718         return this.getChildContainer();
3719     },
3720     
3721     
3722     initEvents : function()
3723     {
3724         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3725         
3726         if (this.autohide) {
3727             
3728             var prevScroll = 0;
3729             var ft = this.el;
3730             
3731             Roo.get(document).on('scroll',function(e) {
3732                 var ns = Roo.get(document).getScroll().top;
3733                 var os = prevScroll;
3734                 prevScroll = ns;
3735                 
3736                 if(ns > os){
3737                     ft.removeClass('slideDown');
3738                     ft.addClass('slideUp');
3739                     return;
3740                 }
3741                 ft.removeClass('slideUp');
3742                 ft.addClass('slideDown');
3743                  
3744               
3745           },this);
3746         }
3747     }    
3748     
3749 });
3750
3751
3752
3753  
3754
3755  /*
3756  * - LGPL
3757  *
3758  * navbar
3759  * 
3760  */
3761
3762 /**
3763  * @class Roo.bootstrap.NavSidebar
3764  * @extends Roo.bootstrap.Navbar
3765  * Bootstrap Sidebar class
3766  * 
3767  * @constructor
3768  * Create a new Sidebar
3769  * @param {Object} config The config object
3770  */
3771
3772
3773 Roo.bootstrap.NavSidebar = function(config){
3774     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3775 };
3776
3777 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3778     
3779     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3780     
3781     getAutoCreate : function(){
3782         
3783         
3784         return  {
3785             tag: 'div',
3786             cls: 'sidebar sidebar-nav'
3787         };
3788     
3789         
3790     }
3791     
3792     
3793     
3794 });
3795
3796
3797
3798  
3799
3800  /*
3801  * - LGPL
3802  *
3803  * nav group
3804  * 
3805  */
3806
3807 /**
3808  * @class Roo.bootstrap.NavGroup
3809  * @extends Roo.bootstrap.Component
3810  * Bootstrap NavGroup class
3811  * @cfg {String} align (left|right)
3812  * @cfg {Boolean} inverse
3813  * @cfg {String} type (nav|pills|tab) default nav
3814  * @cfg {String} navId - reference Id for navbar.
3815
3816  * 
3817  * @constructor
3818  * Create a new nav group
3819  * @param {Object} config The config object
3820  */
3821
3822 Roo.bootstrap.NavGroup = function(config){
3823     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3824     this.navItems = [];
3825    
3826     Roo.bootstrap.NavGroup.register(this);
3827      this.addEvents({
3828         /**
3829              * @event changed
3830              * Fires when the active item changes
3831              * @param {Roo.bootstrap.NavGroup} this
3832              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3833              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3834          */
3835         'changed': true
3836      });
3837     
3838 };
3839
3840 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3841     
3842     align: '',
3843     inverse: false,
3844     form: false,
3845     type: 'nav',
3846     navId : '',
3847     // private
3848     
3849     navItems : false, 
3850     
3851     getAutoCreate : function()
3852     {
3853         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3854         
3855         cfg = {
3856             tag : 'ul',
3857             cls: 'nav' 
3858         };
3859         
3860         if (['tabs','pills'].indexOf(this.type)!==-1) {
3861             cfg.cls += ' nav-' + this.type
3862         } else {
3863             if (this.type!=='nav') {
3864                 Roo.log('nav type must be nav/tabs/pills')
3865             }
3866             cfg.cls += ' navbar-nav'
3867         }
3868         
3869         if (this.parent().sidebar) {
3870             cfg = {
3871                 tag: 'ul',
3872                 cls: 'dashboard-menu sidebar-menu'
3873             };
3874             
3875             return cfg;
3876         }
3877         
3878         if (this.form === true) {
3879             cfg = {
3880                 tag: 'form',
3881                 cls: 'navbar-form'
3882             };
3883             
3884             if (this.align === 'right') {
3885                 cfg.cls += ' navbar-right';
3886             } else {
3887                 cfg.cls += ' navbar-left';
3888             }
3889         }
3890         
3891         if (this.align === 'right') {
3892             cfg.cls += ' navbar-right';
3893         }
3894         
3895         if (this.inverse) {
3896             cfg.cls += ' navbar-inverse';
3897             
3898         }
3899         
3900         
3901         return cfg;
3902     },
3903     /**
3904     * sets the active Navigation item
3905     * @param {Roo.bootstrap.NavItem} the new current navitem
3906     */
3907     setActiveItem : function(item)
3908     {
3909         var prev = false;
3910         Roo.each(this.navItems, function(v){
3911             if (v == item) {
3912                 return ;
3913             }
3914             if (v.isActive()) {
3915                 v.setActive(false, true);
3916                 prev = v;
3917                 
3918             }
3919             
3920         });
3921
3922         item.setActive(true, true);
3923         this.fireEvent('changed', this, item, prev);
3924         
3925         
3926     },
3927     /**
3928     * gets the active Navigation item
3929     * @return {Roo.bootstrap.NavItem} the current navitem
3930     */
3931     getActive : function()
3932     {
3933         
3934         var prev = false;
3935         Roo.each(this.navItems, function(v){
3936             
3937             if (v.isActive()) {
3938                 prev = v;
3939                 
3940             }
3941             
3942         });
3943         return prev;
3944     },
3945     
3946     indexOfNav : function()
3947     {
3948         
3949         var prev = false;
3950         Roo.each(this.navItems, function(v,i){
3951             
3952             if (v.isActive()) {
3953                 prev = i;
3954                 
3955             }
3956             
3957         });
3958         return prev;
3959     },
3960     /**
3961     * adds a Navigation item
3962     * @param {Roo.bootstrap.NavItem} the navitem to add
3963     */
3964     addItem : function(cfg)
3965     {
3966         var cn = new Roo.bootstrap.NavItem(cfg);
3967         this.register(cn);
3968         cn.parentId = this.id;
3969         cn.onRender(this.el, null);
3970         return cn;
3971     },
3972     /**
3973     * register a Navigation item
3974     * @param {Roo.bootstrap.NavItem} the navitem to add
3975     */
3976     register : function(item)
3977     {
3978         this.navItems.push( item);
3979         item.navId = this.navId;
3980     
3981     },
3982     
3983     /**
3984     * clear all the Navigation item
3985     */
3986    
3987     clearAll : function()
3988     {
3989         this.navItems = [];
3990         this.el.dom.innerHTML = '';
3991     },
3992     
3993     getNavItem: function(tabId)
3994     {
3995         var ret = false;
3996         Roo.each(this.navItems, function(e) {
3997             if (e.tabId == tabId) {
3998                ret =  e;
3999                return false;
4000             }
4001             return true;
4002             
4003         });
4004         return ret;
4005     },
4006     
4007     setActiveNext : function()
4008     {
4009         var i = this.indexOfNav(this.getActive());
4010         if (i > this.navItems.length) {
4011             return;
4012         }
4013         this.setActiveItem(this.navItems[i+1]);
4014     },
4015     setActivePrev : function()
4016     {
4017         var i = this.indexOfNav(this.getActive());
4018         if (i  < 1) {
4019             return;
4020         }
4021         this.setActiveItem(this.navItems[i-1]);
4022     },
4023     clearWasActive : function(except) {
4024         Roo.each(this.navItems, function(e) {
4025             if (e.tabId != except.tabId && e.was_active) {
4026                e.was_active = false;
4027                return false;
4028             }
4029             return true;
4030             
4031         });
4032     },
4033     getWasActive : function ()
4034     {
4035         var r = false;
4036         Roo.each(this.navItems, function(e) {
4037             if (e.was_active) {
4038                r = e;
4039                return false;
4040             }
4041             return true;
4042             
4043         });
4044         return r;
4045     }
4046     
4047     
4048 });
4049
4050  
4051 Roo.apply(Roo.bootstrap.NavGroup, {
4052     
4053     groups: {},
4054      /**
4055     * register a Navigation Group
4056     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4057     */
4058     register : function(navgrp)
4059     {
4060         this.groups[navgrp.navId] = navgrp;
4061         
4062     },
4063     /**
4064     * fetch a Navigation Group based on the navigation ID
4065     * @param {string} the navgroup to add
4066     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4067     */
4068     get: function(navId) {
4069         if (typeof(this.groups[navId]) == 'undefined') {
4070             return false;
4071             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4072         }
4073         return this.groups[navId] ;
4074     }
4075     
4076     
4077     
4078 });
4079
4080  /*
4081  * - LGPL
4082  *
4083  * row
4084  * 
4085  */
4086
4087 /**
4088  * @class Roo.bootstrap.NavItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap Navbar.NavItem class
4091  * @cfg {String} href  link to
4092  * @cfg {String} html content of button
4093  * @cfg {String} badge text inside badge
4094  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4095  * @cfg {String} glyphicon name of glyphicon
4096  * @cfg {String} icon name of font awesome icon
4097  * @cfg {Boolean} active Is item active
4098  * @cfg {Boolean} disabled Is item disabled
4099  
4100  * @cfg {Boolean} preventDefault (true | false) default false
4101  * @cfg {String} tabId the tab that this item activates.
4102  * @cfg {String} tagtype (a|span) render as a href or span?
4103  * @cfg {Boolean} animateRef (true|false) link to element default false  
4104   
4105  * @constructor
4106  * Create a new Navbar Item
4107  * @param {Object} config The config object
4108  */
4109 Roo.bootstrap.NavItem = function(config){
4110     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4111     this.addEvents({
4112         // raw events
4113         /**
4114          * @event click
4115          * The raw click event for the entire grid.
4116          * @param {Roo.EventObject} e
4117          */
4118         "click" : true,
4119          /**
4120             * @event changed
4121             * Fires when the active item active state changes
4122             * @param {Roo.bootstrap.NavItem} this
4123             * @param {boolean} state the new state
4124              
4125          */
4126         'changed': true,
4127         /**
4128             * @event scrollto
4129             * Fires when scroll to element
4130             * @param {Roo.bootstrap.NavItem} this
4131             * @param {Object} options
4132             * @param {Roo.EventObject} e
4133              
4134          */
4135         'scrollto': true
4136     });
4137    
4138 };
4139
4140 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4141     
4142     href: false,
4143     html: '',
4144     badge: '',
4145     icon: false,
4146     glyphicon: false,
4147     active: false,
4148     preventDefault : false,
4149     tabId : false,
4150     tagtype : 'a',
4151     disabled : false,
4152     animateRef : false,
4153     was_active : false,
4154     
4155     getAutoCreate : function(){
4156          
4157         var cfg = {
4158             tag: 'li',
4159             cls: 'nav-item'
4160             
4161         };
4162         
4163         if (this.active) {
4164             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4165         }
4166         if (this.disabled) {
4167             cfg.cls += ' disabled';
4168         }
4169         
4170         if (this.href || this.html || this.glyphicon || this.icon) {
4171             cfg.cn = [
4172                 {
4173                     tag: this.tagtype,
4174                     href : this.href || "#",
4175                     html: this.html || ''
4176                 }
4177             ];
4178             
4179             if (this.icon) {
4180                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4181             }
4182
4183             if(this.glyphicon) {
4184                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4185             }
4186             
4187             if (this.menu) {
4188                 
4189                 cfg.cn[0].html += " <span class='caret'></span>";
4190              
4191             }
4192             
4193             if (this.badge !== '') {
4194                  
4195                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4196             }
4197         }
4198         
4199         
4200         
4201         return cfg;
4202     },
4203     initEvents: function() 
4204     {
4205         if (typeof (this.menu) != 'undefined') {
4206             this.menu.parentType = this.xtype;
4207             this.menu.triggerEl = this.el;
4208             this.menu = this.addxtype(Roo.apply({}, this.menu));
4209         }
4210         
4211         this.el.select('a',true).on('click', this.onClick, this);
4212         
4213         if(this.tagtype == 'span'){
4214             this.el.select('span',true).on('click', this.onClick, this);
4215         }
4216        
4217         // at this point parent should be available..
4218         this.parent().register(this);
4219     },
4220     
4221     onClick : function(e)
4222     {
4223         if (e.getTarget('.dropdown-menu-item')) {
4224             // did you click on a menu itemm.... - then don't trigger onclick..
4225             return;
4226         }
4227         
4228         if(
4229                 this.preventDefault || 
4230                 this.href == '#' 
4231         ){
4232             Roo.log("NavItem - prevent Default?");
4233             e.preventDefault();
4234         }
4235         
4236         if (this.disabled) {
4237             return;
4238         }
4239         
4240         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4241         if (tg && tg.transition) {
4242             Roo.log("waiting for the transitionend");
4243             return;
4244         }
4245         
4246         
4247         
4248         //Roo.log("fire event clicked");
4249         if(this.fireEvent('click', this, e) === false){
4250             return;
4251         };
4252         
4253         if(this.tagtype == 'span'){
4254             return;
4255         }
4256         
4257         //Roo.log(this.href);
4258         var ael = this.el.select('a',true).first();
4259         //Roo.log(ael);
4260         
4261         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4262             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4263             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4264                 return; // ignore... - it's a 'hash' to another page.
4265             }
4266             Roo.log("NavItem - prevent Default?");
4267             e.preventDefault();
4268             this.scrollToElement(e);
4269         }
4270         
4271         
4272         var p =  this.parent();
4273    
4274         if (['tabs','pills'].indexOf(p.type)!==-1) {
4275             if (typeof(p.setActiveItem) !== 'undefined') {
4276                 p.setActiveItem(this);
4277             }
4278         }
4279         
4280         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4281         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4282             // remove the collapsed menu expand...
4283             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4284         }
4285     },
4286     
4287     isActive: function () {
4288         return this.active
4289     },
4290     setActive : function(state, fire, is_was_active)
4291     {
4292         if (this.active && !state && this.navId) {
4293             this.was_active = true;
4294             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4295             if (nv) {
4296                 nv.clearWasActive(this);
4297             }
4298             
4299         }
4300         this.active = state;
4301         
4302         if (!state ) {
4303             this.el.removeClass('active');
4304         } else if (!this.el.hasClass('active')) {
4305             this.el.addClass('active');
4306         }
4307         if (fire) {
4308             this.fireEvent('changed', this, state);
4309         }
4310         
4311         // show a panel if it's registered and related..
4312         
4313         if (!this.navId || !this.tabId || !state || is_was_active) {
4314             return;
4315         }
4316         
4317         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4318         if (!tg) {
4319             return;
4320         }
4321         var pan = tg.getPanelByName(this.tabId);
4322         if (!pan) {
4323             return;
4324         }
4325         // if we can not flip to new panel - go back to old nav highlight..
4326         if (false == tg.showPanel(pan)) {
4327             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4328             if (nv) {
4329                 var onav = nv.getWasActive();
4330                 if (onav) {
4331                     onav.setActive(true, false, true);
4332                 }
4333             }
4334             
4335         }
4336         
4337         
4338         
4339     },
4340      // this should not be here...
4341     setDisabled : function(state)
4342     {
4343         this.disabled = state;
4344         if (!state ) {
4345             this.el.removeClass('disabled');
4346         } else if (!this.el.hasClass('disabled')) {
4347             this.el.addClass('disabled');
4348         }
4349         
4350     },
4351     
4352     /**
4353      * Fetch the element to display the tooltip on.
4354      * @return {Roo.Element} defaults to this.el
4355      */
4356     tooltipEl : function()
4357     {
4358         return this.el.select('' + this.tagtype + '', true).first();
4359     },
4360     
4361     scrollToElement : function(e)
4362     {
4363         var c = document.body;
4364         
4365         /*
4366          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4367          */
4368         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4369             c = document.documentElement;
4370         }
4371         
4372         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4373         
4374         if(!target){
4375             return;
4376         }
4377
4378         var o = target.calcOffsetsTo(c);
4379         
4380         var options = {
4381             target : target,
4382             value : o[1]
4383         };
4384         
4385         this.fireEvent('scrollto', this, options, e);
4386         
4387         Roo.get(c).scrollTo('top', options.value, true);
4388         
4389         return;
4390     }
4391 });
4392  
4393
4394  /*
4395  * - LGPL
4396  *
4397  * sidebar item
4398  *
4399  *  li
4400  *    <span> icon </span>
4401  *    <span> text </span>
4402  *    <span>badge </span>
4403  */
4404
4405 /**
4406  * @class Roo.bootstrap.NavSidebarItem
4407  * @extends Roo.bootstrap.NavItem
4408  * Bootstrap Navbar.NavSidebarItem class
4409  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4410  * {bool} open is the menu open
4411  * @constructor
4412  * Create a new Navbar Button
4413  * @param {Object} config The config object
4414  */
4415 Roo.bootstrap.NavSidebarItem = function(config){
4416     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4417     this.addEvents({
4418         // raw events
4419         /**
4420          * @event click
4421          * The raw click event for the entire grid.
4422          * @param {Roo.EventObject} e
4423          */
4424         "click" : true,
4425          /**
4426             * @event changed
4427             * Fires when the active item active state changes
4428             * @param {Roo.bootstrap.NavSidebarItem} this
4429             * @param {boolean} state the new state
4430              
4431          */
4432         'changed': true
4433     });
4434    
4435 };
4436
4437 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4438     
4439     badgeWeight : 'default',
4440     
4441     open: false,
4442     
4443     getAutoCreate : function(){
4444         
4445         
4446         var a = {
4447                 tag: 'a',
4448                 href : this.href || '#',
4449                 cls: '',
4450                 html : '',
4451                 cn : []
4452         };
4453         var cfg = {
4454             tag: 'li',
4455             cls: '',
4456             cn: [ a ]
4457         };
4458         var span = {
4459             tag: 'span',
4460             html : this.html || ''
4461         };
4462         
4463         
4464         if (this.active) {
4465             cfg.cls += ' active';
4466         }
4467         
4468         if (this.disabled) {
4469             cfg.cls += ' disabled';
4470         }
4471         if (this.open) {
4472             cfg.cls += ' open x-open';
4473         }
4474         // left icon..
4475         if (this.glyphicon || this.icon) {
4476             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4477             a.cn.push({ tag : 'i', cls : c }) ;
4478         }
4479         // html..
4480         a.cn.push(span);
4481         // then badge..
4482         if (this.badge !== '') {
4483             
4484             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4485         }
4486         // fi
4487         if (this.menu) {
4488             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4489             a.cls += 'dropdown-toggle treeview' ;
4490             
4491         }
4492         
4493         
4494         
4495         return cfg;
4496          
4497            
4498     },
4499     
4500     initEvents : function()
4501     { 
4502         if (typeof (this.menu) != 'undefined') {
4503             this.menu.parentType = this.xtype;
4504             this.menu.triggerEl = this.el;
4505             this.menu = this.addxtype(Roo.apply({}, this.menu));
4506         }
4507         
4508         this.el.on('click', this.onClick, this);
4509        
4510     
4511         if(this.badge !== ''){
4512  
4513             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4514         }
4515         
4516     },
4517     
4518     onClick : function(e)
4519     {
4520         if(this.disabled){
4521             e.preventDefault();
4522             return;
4523         }
4524         
4525         if(this.preventDefault){
4526             e.preventDefault();
4527         }
4528         
4529         this.fireEvent('click', this);
4530     },
4531     
4532     disable : function()
4533     {
4534         this.setDisabled(true);
4535     },
4536     
4537     enable : function()
4538     {
4539         this.setDisabled(false);
4540     },
4541     
4542     setDisabled : function(state)
4543     {
4544         if(this.disabled == state){
4545             return;
4546         }
4547         
4548         this.disabled = state;
4549         
4550         if (state) {
4551             this.el.addClass('disabled');
4552             return;
4553         }
4554         
4555         this.el.removeClass('disabled');
4556         
4557         return;
4558     },
4559     
4560     setActive : function(state)
4561     {
4562         if(this.active == state){
4563             return;
4564         }
4565         
4566         this.active = state;
4567         
4568         if (state) {
4569             this.el.addClass('active');
4570             return;
4571         }
4572         
4573         this.el.removeClass('active');
4574         
4575         return;
4576     },
4577     
4578     isActive: function () 
4579     {
4580         return this.active;
4581     },
4582     
4583     setBadge : function(str)
4584     {
4585         if(!this.badgeEl){
4586             return;
4587         }
4588         
4589         this.badgeEl.dom.innerHTML = str;
4590     }
4591     
4592    
4593      
4594  
4595 });
4596  
4597
4598  /*
4599  * - LGPL
4600  *
4601  * row
4602  * 
4603  */
4604
4605 /**
4606  * @class Roo.bootstrap.Row
4607  * @extends Roo.bootstrap.Component
4608  * Bootstrap Row class (contains columns...)
4609  * 
4610  * @constructor
4611  * Create a new Row
4612  * @param {Object} config The config object
4613  */
4614
4615 Roo.bootstrap.Row = function(config){
4616     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4617 };
4618
4619 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4620     
4621     getAutoCreate : function(){
4622        return {
4623             cls: 'row clearfix'
4624        };
4625     }
4626     
4627     
4628 });
4629
4630  
4631
4632  /*
4633  * - LGPL
4634  *
4635  * element
4636  * 
4637  */
4638
4639 /**
4640  * @class Roo.bootstrap.Element
4641  * @extends Roo.bootstrap.Component
4642  * Bootstrap Element class
4643  * @cfg {String} html contents of the element
4644  * @cfg {String} tag tag of the element
4645  * @cfg {String} cls class of the element
4646  * @cfg {Boolean} preventDefault (true|false) default false
4647  * @cfg {Boolean} clickable (true|false) default false
4648  * 
4649  * @constructor
4650  * Create a new Element
4651  * @param {Object} config The config object
4652  */
4653
4654 Roo.bootstrap.Element = function(config){
4655     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4656     
4657     this.addEvents({
4658         // raw events
4659         /**
4660          * @event click
4661          * When a element is chick
4662          * @param {Roo.bootstrap.Element} this
4663          * @param {Roo.EventObject} e
4664          */
4665         "click" : true
4666     });
4667 };
4668
4669 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4670     
4671     tag: 'div',
4672     cls: '',
4673     html: '',
4674     preventDefault: false, 
4675     clickable: false,
4676     
4677     getAutoCreate : function(){
4678         
4679         var cfg = {
4680             tag: this.tag,
4681             cls: this.cls,
4682             html: this.html
4683         };
4684         
4685         return cfg;
4686     },
4687     
4688     initEvents: function() 
4689     {
4690         Roo.bootstrap.Element.superclass.initEvents.call(this);
4691         
4692         if(this.clickable){
4693             this.el.on('click', this.onClick, this);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.preventDefault){
4701             e.preventDefault();
4702         }
4703         
4704         this.fireEvent('click', this, e);
4705     },
4706     
4707     getValue : function()
4708     {
4709         return this.el.dom.innerHTML;
4710     },
4711     
4712     setValue : function(value)
4713     {
4714         this.el.dom.innerHTML = value;
4715     }
4716    
4717 });
4718
4719  
4720
4721  /*
4722  * - LGPL
4723  *
4724  * pagination
4725  * 
4726  */
4727
4728 /**
4729  * @class Roo.bootstrap.Pagination
4730  * @extends Roo.bootstrap.Component
4731  * Bootstrap Pagination class
4732  * @cfg {String} size xs | sm | md | lg
4733  * @cfg {Boolean} inverse false | true
4734  * 
4735  * @constructor
4736  * Create a new Pagination
4737  * @param {Object} config The config object
4738  */
4739
4740 Roo.bootstrap.Pagination = function(config){
4741     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4742 };
4743
4744 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4745     
4746     cls: false,
4747     size: false,
4748     inverse: false,
4749     
4750     getAutoCreate : function(){
4751         var cfg = {
4752             tag: 'ul',
4753                 cls: 'pagination'
4754         };
4755         if (this.inverse) {
4756             cfg.cls += ' inverse';
4757         }
4758         if (this.html) {
4759             cfg.html=this.html;
4760         }
4761         if (this.cls) {
4762             cfg.cls += " " + this.cls;
4763         }
4764         return cfg;
4765     }
4766    
4767 });
4768
4769  
4770
4771  /*
4772  * - LGPL
4773  *
4774  * Pagination item
4775  * 
4776  */
4777
4778
4779 /**
4780  * @class Roo.bootstrap.PaginationItem
4781  * @extends Roo.bootstrap.Component
4782  * Bootstrap PaginationItem class
4783  * @cfg {String} html text
4784  * @cfg {String} href the link
4785  * @cfg {Boolean} preventDefault (true | false) default true
4786  * @cfg {Boolean} active (true | false) default false
4787  * @cfg {Boolean} disabled default false
4788  * 
4789  * 
4790  * @constructor
4791  * Create a new PaginationItem
4792  * @param {Object} config The config object
4793  */
4794
4795
4796 Roo.bootstrap.PaginationItem = function(config){
4797     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4798     this.addEvents({
4799         // raw events
4800         /**
4801          * @event click
4802          * The raw click event for the entire grid.
4803          * @param {Roo.EventObject} e
4804          */
4805         "click" : true
4806     });
4807 };
4808
4809 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4810     
4811     href : false,
4812     html : false,
4813     preventDefault: true,
4814     active : false,
4815     cls : false,
4816     disabled: false,
4817     
4818     getAutoCreate : function(){
4819         var cfg= {
4820             tag: 'li',
4821             cn: [
4822                 {
4823                     tag : 'a',
4824                     href : this.href ? this.href : '#',
4825                     html : this.html ? this.html : ''
4826                 }
4827             ]
4828         };
4829         
4830         if(this.cls){
4831             cfg.cls = this.cls;
4832         }
4833         
4834         if(this.disabled){
4835             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4836         }
4837         
4838         if(this.active){
4839             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4840         }
4841         
4842         return cfg;
4843     },
4844     
4845     initEvents: function() {
4846         
4847         this.el.on('click', this.onClick, this);
4848         
4849     },
4850     onClick : function(e)
4851     {
4852         Roo.log('PaginationItem on click ');
4853         if(this.preventDefault){
4854             e.preventDefault();
4855         }
4856         
4857         if(this.disabled){
4858             return;
4859         }
4860         
4861         this.fireEvent('click', this, e);
4862     }
4863    
4864 });
4865
4866  
4867
4868  /*
4869  * - LGPL
4870  *
4871  * slider
4872  * 
4873  */
4874
4875
4876 /**
4877  * @class Roo.bootstrap.Slider
4878  * @extends Roo.bootstrap.Component
4879  * Bootstrap Slider class
4880  *    
4881  * @constructor
4882  * Create a new Slider
4883  * @param {Object} config The config object
4884  */
4885
4886 Roo.bootstrap.Slider = function(config){
4887     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4888 };
4889
4890 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4891     
4892     getAutoCreate : function(){
4893         
4894         var cfg = {
4895             tag: 'div',
4896             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4897             cn: [
4898                 {
4899                     tag: 'a',
4900                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4901                 }
4902             ]
4903         };
4904         
4905         return cfg;
4906     }
4907    
4908 });
4909
4910  /*
4911  * Based on:
4912  * Ext JS Library 1.1.1
4913  * Copyright(c) 2006-2007, Ext JS, LLC.
4914  *
4915  * Originally Released Under LGPL - original licence link has changed is not relivant.
4916  *
4917  * Fork - LGPL
4918  * <script type="text/javascript">
4919  */
4920  
4921
4922 /**
4923  * @class Roo.grid.ColumnModel
4924  * @extends Roo.util.Observable
4925  * This is the default implementation of a ColumnModel used by the Grid. It defines
4926  * the columns in the grid.
4927  * <br>Usage:<br>
4928  <pre><code>
4929  var colModel = new Roo.grid.ColumnModel([
4930         {header: "Ticker", width: 60, sortable: true, locked: true},
4931         {header: "Company Name", width: 150, sortable: true},
4932         {header: "Market Cap.", width: 100, sortable: true},
4933         {header: "$ Sales", width: 100, sortable: true, renderer: money},
4934         {header: "Employees", width: 100, sortable: true, resizable: false}
4935  ]);
4936  </code></pre>
4937  * <p>
4938  
4939  * The config options listed for this class are options which may appear in each
4940  * individual column definition.
4941  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4942  * @constructor
4943  * @param {Object} config An Array of column config objects. See this class's
4944  * config objects for details.
4945 */
4946 Roo.grid.ColumnModel = function(config){
4947         /**
4948      * The config passed into the constructor
4949      */
4950     this.config = config;
4951     this.lookup = {};
4952
4953     // if no id, create one
4954     // if the column does not have a dataIndex mapping,
4955     // map it to the order it is in the config
4956     for(var i = 0, len = config.length; i < len; i++){
4957         var c = config[i];
4958         if(typeof c.dataIndex == "undefined"){
4959             c.dataIndex = i;
4960         }
4961         if(typeof c.renderer == "string"){
4962             c.renderer = Roo.util.Format[c.renderer];
4963         }
4964         if(typeof c.id == "undefined"){
4965             c.id = Roo.id();
4966         }
4967         if(c.editor && c.editor.xtype){
4968             c.editor  = Roo.factory(c.editor, Roo.grid);
4969         }
4970         if(c.editor && c.editor.isFormField){
4971             c.editor = new Roo.grid.GridEditor(c.editor);
4972         }
4973         this.lookup[c.id] = c;
4974     }
4975
4976     /**
4977      * The width of columns which have no width specified (defaults to 100)
4978      * @type Number
4979      */
4980     this.defaultWidth = 100;
4981
4982     /**
4983      * Default sortable of columns which have no sortable specified (defaults to false)
4984      * @type Boolean
4985      */
4986     this.defaultSortable = false;
4987
4988     this.addEvents({
4989         /**
4990              * @event widthchange
4991              * Fires when the width of a column changes.
4992              * @param {ColumnModel} this
4993              * @param {Number} columnIndex The column index
4994              * @param {Number} newWidth The new width
4995              */
4996             "widthchange": true,
4997         /**
4998              * @event headerchange
4999              * Fires when the text of a header changes.
5000              * @param {ColumnModel} this
5001              * @param {Number} columnIndex The column index
5002              * @param {Number} newText The new header text
5003              */
5004             "headerchange": true,
5005         /**
5006              * @event hiddenchange
5007              * Fires when a column is hidden or "unhidden".
5008              * @param {ColumnModel} this
5009              * @param {Number} columnIndex The column index
5010              * @param {Boolean} hidden true if hidden, false otherwise
5011              */
5012             "hiddenchange": true,
5013             /**
5014          * @event columnmoved
5015          * Fires when a column is moved.
5016          * @param {ColumnModel} this
5017          * @param {Number} oldIndex
5018          * @param {Number} newIndex
5019          */
5020         "columnmoved" : true,
5021         /**
5022          * @event columlockchange
5023          * Fires when a column's locked state is changed
5024          * @param {ColumnModel} this
5025          * @param {Number} colIndex
5026          * @param {Boolean} locked true if locked
5027          */
5028         "columnlockchange" : true
5029     });
5030     Roo.grid.ColumnModel.superclass.constructor.call(this);
5031 };
5032 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5033     /**
5034      * @cfg {String} header The header text to display in the Grid view.
5035      */
5036     /**
5037      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5038      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5039      * specified, the column's index is used as an index into the Record's data Array.
5040      */
5041     /**
5042      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5043      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5044      */
5045     /**
5046      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5047      * Defaults to the value of the {@link #defaultSortable} property.
5048      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5049      */
5050     /**
5051      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5052      */
5053     /**
5054      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5055      */
5056     /**
5057      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5058      */
5059     /**
5060      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5061      */
5062     /**
5063      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5064      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5065      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5066      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5067      */
5068        /**
5069      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5070      */
5071     /**
5072      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5073      */
5074     /**
5075      * @cfg {String} cursor (Optional)
5076      */
5077     /**
5078      * @cfg {String} tooltip (Optional)
5079      */
5080     /**
5081      * @cfg {Number} xs (Optional)
5082      */
5083     /**
5084      * @cfg {Number} sm (Optional)
5085      */
5086     /**
5087      * @cfg {Number} md (Optional)
5088      */
5089     /**
5090      * @cfg {Number} lg (Optional)
5091      */
5092     /**
5093      * Returns the id of the column at the specified index.
5094      * @param {Number} index The column index
5095      * @return {String} the id
5096      */
5097     getColumnId : function(index){
5098         return this.config[index].id;
5099     },
5100
5101     /**
5102      * Returns the column for a specified id.
5103      * @param {String} id The column id
5104      * @return {Object} the column
5105      */
5106     getColumnById : function(id){
5107         return this.lookup[id];
5108     },
5109
5110     
5111     /**
5112      * Returns the column for a specified dataIndex.
5113      * @param {String} dataIndex The column dataIndex
5114      * @return {Object|Boolean} the column or false if not found
5115      */
5116     getColumnByDataIndex: function(dataIndex){
5117         var index = this.findColumnIndex(dataIndex);
5118         return index > -1 ? this.config[index] : false;
5119     },
5120     
5121     /**
5122      * Returns the index for a specified column id.
5123      * @param {String} id The column id
5124      * @return {Number} the index, or -1 if not found
5125      */
5126     getIndexById : function(id){
5127         for(var i = 0, len = this.config.length; i < len; i++){
5128             if(this.config[i].id == id){
5129                 return i;
5130             }
5131         }
5132         return -1;
5133     },
5134     
5135     /**
5136      * Returns the index for a specified column dataIndex.
5137      * @param {String} dataIndex The column dataIndex
5138      * @return {Number} the index, or -1 if not found
5139      */
5140     
5141     findColumnIndex : function(dataIndex){
5142         for(var i = 0, len = this.config.length; i < len; i++){
5143             if(this.config[i].dataIndex == dataIndex){
5144                 return i;
5145             }
5146         }
5147         return -1;
5148     },
5149     
5150     
5151     moveColumn : function(oldIndex, newIndex){
5152         var c = this.config[oldIndex];
5153         this.config.splice(oldIndex, 1);
5154         this.config.splice(newIndex, 0, c);
5155         this.dataMap = null;
5156         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5157     },
5158
5159     isLocked : function(colIndex){
5160         return this.config[colIndex].locked === true;
5161     },
5162
5163     setLocked : function(colIndex, value, suppressEvent){
5164         if(this.isLocked(colIndex) == value){
5165             return;
5166         }
5167         this.config[colIndex].locked = value;
5168         if(!suppressEvent){
5169             this.fireEvent("columnlockchange", this, colIndex, value);
5170         }
5171     },
5172
5173     getTotalLockedWidth : function(){
5174         var totalWidth = 0;
5175         for(var i = 0; i < this.config.length; i++){
5176             if(this.isLocked(i) && !this.isHidden(i)){
5177                 this.totalWidth += this.getColumnWidth(i);
5178             }
5179         }
5180         return totalWidth;
5181     },
5182
5183     getLockedCount : function(){
5184         for(var i = 0, len = this.config.length; i < len; i++){
5185             if(!this.isLocked(i)){
5186                 return i;
5187             }
5188         }
5189         
5190         return this.config.length;
5191     },
5192
5193     /**
5194      * Returns the number of columns.
5195      * @return {Number}
5196      */
5197     getColumnCount : function(visibleOnly){
5198         if(visibleOnly === true){
5199             var c = 0;
5200             for(var i = 0, len = this.config.length; i < len; i++){
5201                 if(!this.isHidden(i)){
5202                     c++;
5203                 }
5204             }
5205             return c;
5206         }
5207         return this.config.length;
5208     },
5209
5210     /**
5211      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5212      * @param {Function} fn
5213      * @param {Object} scope (optional)
5214      * @return {Array} result
5215      */
5216     getColumnsBy : function(fn, scope){
5217         var r = [];
5218         for(var i = 0, len = this.config.length; i < len; i++){
5219             var c = this.config[i];
5220             if(fn.call(scope||this, c, i) === true){
5221                 r[r.length] = c;
5222             }
5223         }
5224         return r;
5225     },
5226
5227     /**
5228      * Returns true if the specified column is sortable.
5229      * @param {Number} col The column index
5230      * @return {Boolean}
5231      */
5232     isSortable : function(col){
5233         if(typeof this.config[col].sortable == "undefined"){
5234             return this.defaultSortable;
5235         }
5236         return this.config[col].sortable;
5237     },
5238
5239     /**
5240      * Returns the rendering (formatting) function defined for the column.
5241      * @param {Number} col The column index.
5242      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5243      */
5244     getRenderer : function(col){
5245         if(!this.config[col].renderer){
5246             return Roo.grid.ColumnModel.defaultRenderer;
5247         }
5248         return this.config[col].renderer;
5249     },
5250
5251     /**
5252      * Sets the rendering (formatting) function for a column.
5253      * @param {Number} col The column index
5254      * @param {Function} fn The function to use to process the cell's raw data
5255      * to return HTML markup for the grid view. The render function is called with
5256      * the following parameters:<ul>
5257      * <li>Data value.</li>
5258      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5259      * <li>css A CSS style string to apply to the table cell.</li>
5260      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5261      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5262      * <li>Row index</li>
5263      * <li>Column index</li>
5264      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5265      */
5266     setRenderer : function(col, fn){
5267         this.config[col].renderer = fn;
5268     },
5269
5270     /**
5271      * Returns the width for the specified column.
5272      * @param {Number} col The column index
5273      * @return {Number}
5274      */
5275     getColumnWidth : function(col){
5276         return this.config[col].width * 1 || this.defaultWidth;
5277     },
5278
5279     /**
5280      * Sets the width for a column.
5281      * @param {Number} col The column index
5282      * @param {Number} width The new width
5283      */
5284     setColumnWidth : function(col, width, suppressEvent){
5285         this.config[col].width = width;
5286         this.totalWidth = null;
5287         if(!suppressEvent){
5288              this.fireEvent("widthchange", this, col, width);
5289         }
5290     },
5291
5292     /**
5293      * Returns the total width of all columns.
5294      * @param {Boolean} includeHidden True to include hidden column widths
5295      * @return {Number}
5296      */
5297     getTotalWidth : function(includeHidden){
5298         if(!this.totalWidth){
5299             this.totalWidth = 0;
5300             for(var i = 0, len = this.config.length; i < len; i++){
5301                 if(includeHidden || !this.isHidden(i)){
5302                     this.totalWidth += this.getColumnWidth(i);
5303                 }
5304             }
5305         }
5306         return this.totalWidth;
5307     },
5308
5309     /**
5310      * Returns the header for the specified column.
5311      * @param {Number} col The column index
5312      * @return {String}
5313      */
5314     getColumnHeader : function(col){
5315         return this.config[col].header;
5316     },
5317
5318     /**
5319      * Sets the header for a column.
5320      * @param {Number} col The column index
5321      * @param {String} header The new header
5322      */
5323     setColumnHeader : function(col, header){
5324         this.config[col].header = header;
5325         this.fireEvent("headerchange", this, col, header);
5326     },
5327
5328     /**
5329      * Returns the tooltip for the specified column.
5330      * @param {Number} col The column index
5331      * @return {String}
5332      */
5333     getColumnTooltip : function(col){
5334             return this.config[col].tooltip;
5335     },
5336     /**
5337      * Sets the tooltip for a column.
5338      * @param {Number} col The column index
5339      * @param {String} tooltip The new tooltip
5340      */
5341     setColumnTooltip : function(col, tooltip){
5342             this.config[col].tooltip = tooltip;
5343     },
5344
5345     /**
5346      * Returns the dataIndex for the specified column.
5347      * @param {Number} col The column index
5348      * @return {Number}
5349      */
5350     getDataIndex : function(col){
5351         return this.config[col].dataIndex;
5352     },
5353
5354     /**
5355      * Sets the dataIndex for a column.
5356      * @param {Number} col The column index
5357      * @param {Number} dataIndex The new dataIndex
5358      */
5359     setDataIndex : function(col, dataIndex){
5360         this.config[col].dataIndex = dataIndex;
5361     },
5362
5363     
5364     
5365     /**
5366      * Returns true if the cell is editable.
5367      * @param {Number} colIndex The column index
5368      * @param {Number} rowIndex The row index - this is nto actually used..?
5369      * @return {Boolean}
5370      */
5371     isCellEditable : function(colIndex, rowIndex){
5372         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5373     },
5374
5375     /**
5376      * Returns the editor defined for the cell/column.
5377      * return false or null to disable editing.
5378      * @param {Number} colIndex The column index
5379      * @param {Number} rowIndex The row index
5380      * @return {Object}
5381      */
5382     getCellEditor : function(colIndex, rowIndex){
5383         return this.config[colIndex].editor;
5384     },
5385
5386     /**
5387      * Sets if a column is editable.
5388      * @param {Number} col The column index
5389      * @param {Boolean} editable True if the column is editable
5390      */
5391     setEditable : function(col, editable){
5392         this.config[col].editable = editable;
5393     },
5394
5395
5396     /**
5397      * Returns true if the column is hidden.
5398      * @param {Number} colIndex The column index
5399      * @return {Boolean}
5400      */
5401     isHidden : function(colIndex){
5402         return this.config[colIndex].hidden;
5403     },
5404
5405
5406     /**
5407      * Returns true if the column width cannot be changed
5408      */
5409     isFixed : function(colIndex){
5410         return this.config[colIndex].fixed;
5411     },
5412
5413     /**
5414      * Returns true if the column can be resized
5415      * @return {Boolean}
5416      */
5417     isResizable : function(colIndex){
5418         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5419     },
5420     /**
5421      * Sets if a column is hidden.
5422      * @param {Number} colIndex The column index
5423      * @param {Boolean} hidden True if the column is hidden
5424      */
5425     setHidden : function(colIndex, hidden){
5426         this.config[colIndex].hidden = hidden;
5427         this.totalWidth = null;
5428         this.fireEvent("hiddenchange", this, colIndex, hidden);
5429     },
5430
5431     /**
5432      * Sets the editor for a column.
5433      * @param {Number} col The column index
5434      * @param {Object} editor The editor object
5435      */
5436     setEditor : function(col, editor){
5437         this.config[col].editor = editor;
5438     }
5439 });
5440
5441 Roo.grid.ColumnModel.defaultRenderer = function(value){
5442         if(typeof value == "string" && value.length < 1){
5443             return "&#160;";
5444         }
5445         return value;
5446 };
5447
5448 // Alias for backwards compatibility
5449 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5450 /*
5451  * Based on:
5452  * Ext JS Library 1.1.1
5453  * Copyright(c) 2006-2007, Ext JS, LLC.
5454  *
5455  * Originally Released Under LGPL - original licence link has changed is not relivant.
5456  *
5457  * Fork - LGPL
5458  * <script type="text/javascript">
5459  */
5460  
5461 /**
5462  * @class Roo.LoadMask
5463  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5464  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5465  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5466  * element's UpdateManager load indicator and will be destroyed after the initial load.
5467  * @constructor
5468  * Create a new LoadMask
5469  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5470  * @param {Object} config The config object
5471  */
5472 Roo.LoadMask = function(el, config){
5473     this.el = Roo.get(el);
5474     Roo.apply(this, config);
5475     if(this.store){
5476         this.store.on('beforeload', this.onBeforeLoad, this);
5477         this.store.on('load', this.onLoad, this);
5478         this.store.on('loadexception', this.onLoadException, this);
5479         this.removeMask = false;
5480     }else{
5481         var um = this.el.getUpdateManager();
5482         um.showLoadIndicator = false; // disable the default indicator
5483         um.on('beforeupdate', this.onBeforeLoad, this);
5484         um.on('update', this.onLoad, this);
5485         um.on('failure', this.onLoad, this);
5486         this.removeMask = true;
5487     }
5488 };
5489
5490 Roo.LoadMask.prototype = {
5491     /**
5492      * @cfg {Boolean} removeMask
5493      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5494      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5495      */
5496     /**
5497      * @cfg {String} msg
5498      * The text to display in a centered loading message box (defaults to 'Loading...')
5499      */
5500     msg : 'Loading...',
5501     /**
5502      * @cfg {String} msgCls
5503      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5504      */
5505     msgCls : 'x-mask-loading',
5506
5507     /**
5508      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5509      * @type Boolean
5510      */
5511     disabled: false,
5512
5513     /**
5514      * Disables the mask to prevent it from being displayed
5515      */
5516     disable : function(){
5517        this.disabled = true;
5518     },
5519
5520     /**
5521      * Enables the mask so that it can be displayed
5522      */
5523     enable : function(){
5524         this.disabled = false;
5525     },
5526     
5527     onLoadException : function()
5528     {
5529         Roo.log(arguments);
5530         
5531         if (typeof(arguments[3]) != 'undefined') {
5532             Roo.MessageBox.alert("Error loading",arguments[3]);
5533         } 
5534         /*
5535         try {
5536             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5537                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5538             }   
5539         } catch(e) {
5540             
5541         }
5542         */
5543     
5544         
5545         
5546         this.el.unmask(this.removeMask);
5547     },
5548     // private
5549     onLoad : function()
5550     {
5551         this.el.unmask(this.removeMask);
5552     },
5553
5554     // private
5555     onBeforeLoad : function(){
5556         if(!this.disabled){
5557             this.el.mask(this.msg, this.msgCls);
5558         }
5559     },
5560
5561     // private
5562     destroy : function(){
5563         if(this.store){
5564             this.store.un('beforeload', this.onBeforeLoad, this);
5565             this.store.un('load', this.onLoad, this);
5566             this.store.un('loadexception', this.onLoadException, this);
5567         }else{
5568             var um = this.el.getUpdateManager();
5569             um.un('beforeupdate', this.onBeforeLoad, this);
5570             um.un('update', this.onLoad, this);
5571             um.un('failure', this.onLoad, this);
5572         }
5573     }
5574 };/*
5575  * - LGPL
5576  *
5577  * table
5578  * 
5579  */
5580
5581 /**
5582  * @class Roo.bootstrap.Table
5583  * @extends Roo.bootstrap.Component
5584  * Bootstrap Table class
5585  * @cfg {String} cls table class
5586  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5587  * @cfg {String} bgcolor Specifies the background color for a table
5588  * @cfg {Number} border Specifies whether the table cells should have borders or not
5589  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5590  * @cfg {Number} cellspacing Specifies the space between cells
5591  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5592  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5593  * @cfg {String} sortable Specifies that the table should be sortable
5594  * @cfg {String} summary Specifies a summary of the content of a table
5595  * @cfg {Number} width Specifies the width of a table
5596  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5597  * 
5598  * @cfg {boolean} striped Should the rows be alternative striped
5599  * @cfg {boolean} bordered Add borders to the table
5600  * @cfg {boolean} hover Add hover highlighting
5601  * @cfg {boolean} condensed Format condensed
5602  * @cfg {boolean} responsive Format condensed
5603  * @cfg {Boolean} loadMask (true|false) default false
5604  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5605  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5606  * @cfg {Boolean} rowSelection (true|false) default false
5607  * @cfg {Boolean} cellSelection (true|false) default false
5608  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5609  
5610  * 
5611  * @constructor
5612  * Create a new Table
5613  * @param {Object} config The config object
5614  */
5615
5616 Roo.bootstrap.Table = function(config){
5617     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5618     
5619     // BC...
5620     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5621     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5622     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5623     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5624     
5625     
5626     if (this.sm) {
5627         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5628         this.sm = this.selModel;
5629         this.sm.xmodule = this.xmodule || false;
5630     }
5631     if (this.cm && typeof(this.cm.config) == 'undefined') {
5632         this.colModel = new Roo.grid.ColumnModel(this.cm);
5633         this.cm = this.colModel;
5634         this.cm.xmodule = this.xmodule || false;
5635     }
5636     if (this.store) {
5637         this.store= Roo.factory(this.store, Roo.data);
5638         this.ds = this.store;
5639         this.ds.xmodule = this.xmodule || false;
5640          
5641     }
5642     if (this.footer && this.store) {
5643         this.footer.dataSource = this.ds;
5644         this.footer = Roo.factory(this.footer);
5645     }
5646     
5647     /** @private */
5648     this.addEvents({
5649         /**
5650          * @event cellclick
5651          * Fires when a cell is clicked
5652          * @param {Roo.bootstrap.Table} this
5653          * @param {Roo.Element} el
5654          * @param {Number} rowIndex
5655          * @param {Number} columnIndex
5656          * @param {Roo.EventObject} e
5657          */
5658         "cellclick" : true,
5659         /**
5660          * @event celldblclick
5661          * Fires when a cell is double clicked
5662          * @param {Roo.bootstrap.Table} this
5663          * @param {Roo.Element} el
5664          * @param {Number} rowIndex
5665          * @param {Number} columnIndex
5666          * @param {Roo.EventObject} e
5667          */
5668         "celldblclick" : true,
5669         /**
5670          * @event rowclick
5671          * Fires when a row is clicked
5672          * @param {Roo.bootstrap.Table} this
5673          * @param {Roo.Element} el
5674          * @param {Number} rowIndex
5675          * @param {Roo.EventObject} e
5676          */
5677         "rowclick" : true,
5678         /**
5679          * @event rowdblclick
5680          * Fires when a row is double clicked
5681          * @param {Roo.bootstrap.Table} this
5682          * @param {Roo.Element} el
5683          * @param {Number} rowIndex
5684          * @param {Roo.EventObject} e
5685          */
5686         "rowdblclick" : true,
5687         /**
5688          * @event mouseover
5689          * Fires when a mouseover occur
5690          * @param {Roo.bootstrap.Table} this
5691          * @param {Roo.Element} el
5692          * @param {Number} rowIndex
5693          * @param {Number} columnIndex
5694          * @param {Roo.EventObject} e
5695          */
5696         "mouseover" : true,
5697         /**
5698          * @event mouseout
5699          * Fires when a mouseout occur
5700          * @param {Roo.bootstrap.Table} this
5701          * @param {Roo.Element} el
5702          * @param {Number} rowIndex
5703          * @param {Number} columnIndex
5704          * @param {Roo.EventObject} e
5705          */
5706         "mouseout" : true,
5707         /**
5708          * @event rowclass
5709          * Fires when a row is rendered, so you can change add a style to it.
5710          * @param {Roo.bootstrap.Table} this
5711          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5712          */
5713         'rowclass' : true,
5714           /**
5715          * @event rowsrendered
5716          * Fires when all the  rows have been rendered
5717          * @param {Roo.bootstrap.Table} this
5718          */
5719         'rowsrendered' : true
5720         
5721     });
5722 };
5723
5724 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5725     
5726     cls: false,
5727     align: false,
5728     bgcolor: false,
5729     border: false,
5730     cellpadding: false,
5731     cellspacing: false,
5732     frame: false,
5733     rules: false,
5734     sortable: false,
5735     summary: false,
5736     width: false,
5737     striped : false,
5738     bordered: false,
5739     hover:  false,
5740     condensed : false,
5741     responsive : false,
5742     sm : false,
5743     cm : false,
5744     store : false,
5745     loadMask : false,
5746     footerShow : true,
5747     headerShow : true,
5748   
5749     rowSelection : false,
5750     cellSelection : false,
5751     layout : false,
5752     
5753     // Roo.Element - the tbody
5754     mainBody: false, 
5755     
5756     getAutoCreate : function(){
5757         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5758         
5759         cfg = {
5760             tag: 'table',
5761             cls : 'table',
5762             cn : []
5763         };
5764             
5765         if (this.striped) {
5766             cfg.cls += ' table-striped';
5767         }
5768         
5769         if (this.hover) {
5770             cfg.cls += ' table-hover';
5771         }
5772         if (this.bordered) {
5773             cfg.cls += ' table-bordered';
5774         }
5775         if (this.condensed) {
5776             cfg.cls += ' table-condensed';
5777         }
5778         if (this.responsive) {
5779             cfg.cls += ' table-responsive';
5780         }
5781         
5782         if (this.cls) {
5783             cfg.cls+=  ' ' +this.cls;
5784         }
5785         
5786         // this lot should be simplifed...
5787         
5788         if (this.align) {
5789             cfg.align=this.align;
5790         }
5791         if (this.bgcolor) {
5792             cfg.bgcolor=this.bgcolor;
5793         }
5794         if (this.border) {
5795             cfg.border=this.border;
5796         }
5797         if (this.cellpadding) {
5798             cfg.cellpadding=this.cellpadding;
5799         }
5800         if (this.cellspacing) {
5801             cfg.cellspacing=this.cellspacing;
5802         }
5803         if (this.frame) {
5804             cfg.frame=this.frame;
5805         }
5806         if (this.rules) {
5807             cfg.rules=this.rules;
5808         }
5809         if (this.sortable) {
5810             cfg.sortable=this.sortable;
5811         }
5812         if (this.summary) {
5813             cfg.summary=this.summary;
5814         }
5815         if (this.width) {
5816             cfg.width=this.width;
5817         }
5818         if (this.layout) {
5819             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5820         }
5821         
5822         if(this.store || this.cm){
5823             if(this.headerShow){
5824                 cfg.cn.push(this.renderHeader());
5825             }
5826             
5827             cfg.cn.push(this.renderBody());
5828             
5829             if(this.footerShow){
5830                 cfg.cn.push(this.renderFooter());
5831             }
5832             
5833             cfg.cls+=  ' TableGrid';
5834         }
5835         
5836         return { cn : [ cfg ] };
5837     },
5838     
5839     initEvents : function()
5840     {   
5841         if(!this.store || !this.cm){
5842             return;
5843         }
5844         
5845         //Roo.log('initEvents with ds!!!!');
5846         
5847         this.mainBody = this.el.select('tbody', true).first();
5848         
5849         
5850         var _this = this;
5851         
5852         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5853             e.on('click', _this.sort, _this);
5854         });
5855         
5856         this.el.on("click", this.onClick, this);
5857         this.el.on("dblclick", this.onDblClick, this);
5858         
5859         // why is this done????? = it breaks dialogs??
5860         //this.parent().el.setStyle('position', 'relative');
5861         
5862         
5863         if (this.footer) {
5864             this.footer.parentId = this.id;
5865             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5866         }
5867         
5868         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5869         
5870         this.store.on('load', this.onLoad, this);
5871         this.store.on('beforeload', this.onBeforeLoad, this);
5872         this.store.on('update', this.onUpdate, this);
5873         this.store.on('add', this.onAdd, this);
5874         
5875     },
5876     
5877     onMouseover : function(e, el)
5878     {
5879         var cell = Roo.get(el);
5880         
5881         if(!cell){
5882             return;
5883         }
5884         
5885         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5886             cell = cell.findParent('td', false, true);
5887         }
5888         
5889         var row = cell.findParent('tr', false, true);
5890         var cellIndex = cell.dom.cellIndex;
5891         var rowIndex = row.dom.rowIndex - 1; // start from 0
5892         
5893         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5894         
5895     },
5896     
5897     onMouseout : function(e, el)
5898     {
5899         var cell = Roo.get(el);
5900         
5901         if(!cell){
5902             return;
5903         }
5904         
5905         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5906             cell = cell.findParent('td', false, true);
5907         }
5908         
5909         var row = cell.findParent('tr', false, true);
5910         var cellIndex = cell.dom.cellIndex;
5911         var rowIndex = row.dom.rowIndex - 1; // start from 0
5912         
5913         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5914         
5915     },
5916     
5917     onClick : function(e, el)
5918     {
5919         var cell = Roo.get(el);
5920         
5921         if(!cell || (!this.cellSelection && !this.rowSelection)){
5922             return;
5923         }
5924         
5925         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5926             cell = cell.findParent('td', false, true);
5927         }
5928         
5929         if(!cell || typeof(cell) == 'undefined'){
5930             return;
5931         }
5932         
5933         var row = cell.findParent('tr', false, true);
5934         
5935         if(!row || typeof(row) == 'undefined'){
5936             return;
5937         }
5938         
5939         var cellIndex = cell.dom.cellIndex;
5940         var rowIndex = this.getRowIndex(row);
5941         
5942         // why??? - should these not be based on SelectionModel?
5943         if(this.cellSelection){
5944             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5945         }
5946         
5947         if(this.rowSelection){
5948             this.fireEvent('rowclick', this, row, rowIndex, e);
5949         }
5950         
5951         
5952     },
5953     
5954     onDblClick : function(e,el)
5955     {
5956         var cell = Roo.get(el);
5957         
5958         if(!cell || (!this.CellSelection && !this.RowSelection)){
5959             return;
5960         }
5961         
5962         if(e.getTarget().nodeName.toLowerCase() != 'td'){
5963             cell = cell.findParent('td', false, true);
5964         }
5965         
5966         if(!cell || typeof(cell) == 'undefined'){
5967             return;
5968         }
5969         
5970         var row = cell.findParent('tr', false, true);
5971         
5972         if(!row || typeof(row) == 'undefined'){
5973             return;
5974         }
5975         
5976         var cellIndex = cell.dom.cellIndex;
5977         var rowIndex = this.getRowIndex(row);
5978         
5979         if(this.CellSelection){
5980             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5981         }
5982         
5983         if(this.RowSelection){
5984             this.fireEvent('rowdblclick', this, row, rowIndex, e);
5985         }
5986     },
5987     
5988     sort : function(e,el)
5989     {
5990         var col = Roo.get(el);
5991         
5992         if(!col.hasClass('sortable')){
5993             return;
5994         }
5995         
5996         var sort = col.attr('sort');
5997         var dir = 'ASC';
5998         
5999         if(col.hasClass('glyphicon-arrow-up')){
6000             dir = 'DESC';
6001         }
6002         
6003         this.store.sortInfo = {field : sort, direction : dir};
6004         
6005         if (this.footer) {
6006             Roo.log("calling footer first");
6007             this.footer.onClick('first');
6008         } else {
6009         
6010             this.store.load({ params : { start : 0 } });
6011         }
6012     },
6013     
6014     renderHeader : function()
6015     {
6016         var header = {
6017             tag: 'thead',
6018             cn : []
6019         };
6020         
6021         var cm = this.cm;
6022         
6023         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6024             
6025             var config = cm.config[i];
6026             
6027             var c = {
6028                 tag: 'th',
6029                 style : '',
6030                 html: cm.getColumnHeader(i)
6031             };
6032             
6033             var hh = '';
6034             
6035             if(typeof(config.lgHeader) != 'undefined'){
6036                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6037             }
6038             
6039             if(typeof(config.mdHeader) != 'undefined'){
6040                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6041             }
6042             
6043             if(typeof(config.smHeader) != 'undefined'){
6044                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6045             }
6046             
6047             if(typeof(config.xsHeader) != 'undefined'){
6048                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6049             }
6050             
6051             if(hh.length){
6052                 c.html = hh;
6053             }
6054             
6055             if(typeof(config.tooltip) != 'undefined'){
6056                 c.tooltip = config.tooltip;
6057             }
6058             
6059             if(typeof(config.colspan) != 'undefined'){
6060                 c.colspan = config.colspan;
6061             }
6062             
6063             if(typeof(config.hidden) != 'undefined' && config.hidden){
6064                 c.style += ' display:none;';
6065             }
6066             
6067             if(typeof(config.dataIndex) != 'undefined'){
6068                 c.sort = config.dataIndex;
6069             }
6070             
6071             if(typeof(config.sortable) != 'undefined' && config.sortable){
6072                 c.cls = 'sortable';
6073             }
6074             
6075             if(typeof(config.align) != 'undefined' && config.align.length){
6076                 c.style += ' text-align:' + config.align + ';';
6077             }
6078             
6079             if(typeof(config.width) != 'undefined'){
6080                 c.style += ' width:' + config.width + 'px;';
6081             }
6082             
6083             if(typeof(config.cls) != 'undefined'){
6084                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6085             }
6086             
6087             ['xs','sm','md','lg'].map(function(size){
6088                 
6089                 if(typeof(config[size]) == 'undefined'){
6090                     return;
6091                 }
6092                 
6093                 if (!config[size]) { // 0 = hidden
6094                     c.cls += ' hidden-' + size;
6095                     return;
6096                 }
6097                 
6098                 c.cls += ' col-' + size + '-' + config[size];
6099
6100             });
6101             
6102             header.cn.push(c)
6103         }
6104         
6105         return header;
6106     },
6107     
6108     renderBody : function()
6109     {
6110         var body = {
6111             tag: 'tbody',
6112             cn : [
6113                 {
6114                     tag: 'tr',
6115                     cn : [
6116                         {
6117                             tag : 'td',
6118                             colspan :  this.cm.getColumnCount()
6119                         }
6120                     ]
6121                 }
6122             ]
6123         };
6124         
6125         return body;
6126     },
6127     
6128     renderFooter : function()
6129     {
6130         var footer = {
6131             tag: 'tfoot',
6132             cn : [
6133                 {
6134                     tag: 'tr',
6135                     cn : [
6136                         {
6137                             tag : 'td',
6138                             colspan :  this.cm.getColumnCount()
6139                         }
6140                     ]
6141                 }
6142             ]
6143         };
6144         
6145         return footer;
6146     },
6147     
6148     
6149     
6150     onLoad : function()
6151     {
6152 //        Roo.log('ds onload');
6153         this.clear();
6154         
6155         var _this = this;
6156         var cm = this.cm;
6157         var ds = this.store;
6158         
6159         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6160             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
6161             
6162             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6163                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
6164             }
6165             
6166             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6167                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
6168             }
6169         });
6170         
6171         var tbody =  this.mainBody;
6172               
6173         if(ds.getCount() > 0){
6174             ds.data.each(function(d,rowIndex){
6175                 var row =  this.renderRow(cm, ds, rowIndex);
6176                 
6177                 tbody.createChild(row);
6178                 
6179                 var _this = this;
6180                 
6181                 if(row.cellObjects.length){
6182                     Roo.each(row.cellObjects, function(r){
6183                         _this.renderCellObject(r);
6184                     })
6185                 }
6186                 
6187             }, this);
6188         }
6189         
6190         Roo.each(this.el.select('tbody td', true).elements, function(e){
6191             e.on('mouseover', _this.onMouseover, _this);
6192         });
6193         
6194         Roo.each(this.el.select('tbody td', true).elements, function(e){
6195             e.on('mouseout', _this.onMouseout, _this);
6196         });
6197         this.fireEvent('rowsrendered', this);
6198         //if(this.loadMask){
6199         //    this.maskEl.hide();
6200         //}
6201     },
6202     
6203     
6204     onUpdate : function(ds,record)
6205     {
6206         this.refreshRow(record);
6207     },
6208     
6209     onRemove : function(ds, record, index, isUpdate){
6210         if(isUpdate !== true){
6211             this.fireEvent("beforerowremoved", this, index, record);
6212         }
6213         var bt = this.mainBody.dom;
6214         
6215         var rows = this.el.select('tbody > tr', true).elements;
6216         
6217         if(typeof(rows[index]) != 'undefined'){
6218             bt.removeChild(rows[index].dom);
6219         }
6220         
6221 //        if(bt.rows[index]){
6222 //            bt.removeChild(bt.rows[index]);
6223 //        }
6224         
6225         if(isUpdate !== true){
6226             //this.stripeRows(index);
6227             //this.syncRowHeights(index, index);
6228             //this.layout();
6229             this.fireEvent("rowremoved", this, index, record);
6230         }
6231     },
6232     
6233     onAdd : function(ds, records, rowIndex)
6234     {
6235         //Roo.log('on Add called');
6236         // - note this does not handle multiple adding very well..
6237         var bt = this.mainBody.dom;
6238         for (var i =0 ; i < records.length;i++) {
6239             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6240             //Roo.log(records[i]);
6241             //Roo.log(this.store.getAt(rowIndex+i));
6242             this.insertRow(this.store, rowIndex + i, false);
6243             return;
6244         }
6245         
6246     },
6247     
6248     
6249     refreshRow : function(record){
6250         var ds = this.store, index;
6251         if(typeof record == 'number'){
6252             index = record;
6253             record = ds.getAt(index);
6254         }else{
6255             index = ds.indexOf(record);
6256         }
6257         this.insertRow(ds, index, true);
6258         this.onRemove(ds, record, index+1, true);
6259         //this.syncRowHeights(index, index);
6260         //this.layout();
6261         this.fireEvent("rowupdated", this, index, record);
6262     },
6263     
6264     insertRow : function(dm, rowIndex, isUpdate){
6265         
6266         if(!isUpdate){
6267             this.fireEvent("beforerowsinserted", this, rowIndex);
6268         }
6269             //var s = this.getScrollState();
6270         var row = this.renderRow(this.cm, this.store, rowIndex);
6271         // insert before rowIndex..
6272         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6273         
6274         var _this = this;
6275                 
6276         if(row.cellObjects.length){
6277             Roo.each(row.cellObjects, function(r){
6278                 _this.renderCellObject(r);
6279             })
6280         }
6281             
6282         if(!isUpdate){
6283             this.fireEvent("rowsinserted", this, rowIndex);
6284             //this.syncRowHeights(firstRow, lastRow);
6285             //this.stripeRows(firstRow);
6286             //this.layout();
6287         }
6288         
6289     },
6290     
6291     
6292     getRowDom : function(rowIndex)
6293     {
6294         var rows = this.el.select('tbody > tr', true).elements;
6295         
6296         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6297         
6298     },
6299     // returns the object tree for a tr..
6300   
6301     
6302     renderRow : function(cm, ds, rowIndex) 
6303     {
6304         
6305         var d = ds.getAt(rowIndex);
6306         
6307         var row = {
6308             tag : 'tr',
6309             cn : []
6310         };
6311             
6312         var cellObjects = [];
6313         
6314         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6315             var config = cm.config[i];
6316             
6317             var renderer = cm.getRenderer(i);
6318             var value = '';
6319             var id = false;
6320             
6321             if(typeof(renderer) !== 'undefined'){
6322                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6323             }
6324             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6325             // and are rendered into the cells after the row is rendered - using the id for the element.
6326             
6327             if(typeof(value) === 'object'){
6328                 id = Roo.id();
6329                 cellObjects.push({
6330                     container : id,
6331                     cfg : value 
6332                 })
6333             }
6334             
6335             var rowcfg = {
6336                 record: d,
6337                 rowIndex : rowIndex,
6338                 colIndex : i,
6339                 rowClass : ''
6340             };
6341
6342             this.fireEvent('rowclass', this, rowcfg);
6343             
6344             var td = {
6345                 tag: 'td',
6346                 cls : rowcfg.rowClass,
6347                 style: '',
6348                 html: (typeof(value) === 'object') ? '' : value
6349             };
6350             
6351             if (id) {
6352                 td.id = id;
6353             }
6354             
6355             if(typeof(config.colspan) != 'undefined'){
6356                 td.colspan = config.colspan;
6357             }
6358             
6359             if(typeof(config.hidden) != 'undefined' && config.hidden){
6360                 td.style += ' display:none;';
6361             }
6362             
6363             if(typeof(config.align) != 'undefined' && config.align.length){
6364                 td.style += ' text-align:' + config.align + ';';
6365             }
6366             
6367             if(typeof(config.width) != 'undefined'){
6368                 td.style += ' width:' +  config.width + 'px;';
6369             }
6370             
6371             if(typeof(config.cursor) != 'undefined'){
6372                 td.style += ' cursor:' +  config.cursor + ';';
6373             }
6374             
6375             if(typeof(config.cls) != 'undefined'){
6376                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6377             }
6378             
6379             ['xs','sm','md','lg'].map(function(size){
6380                 
6381                 if(typeof(config[size]) == 'undefined'){
6382                     return;
6383                 }
6384                 
6385                 if (!config[size]) { // 0 = hidden
6386                     td.cls += ' hidden-' + size;
6387                     return;
6388                 }
6389                 
6390                 td.cls += ' col-' + size + '-' + config[size];
6391
6392             });
6393              
6394             row.cn.push(td);
6395            
6396         }
6397         
6398         row.cellObjects = cellObjects;
6399         
6400         return row;
6401           
6402     },
6403     
6404     
6405     
6406     onBeforeLoad : function()
6407     {
6408         //Roo.log('ds onBeforeLoad');
6409         
6410         //this.clear();
6411         
6412         //if(this.loadMask){
6413         //    this.maskEl.show();
6414         //}
6415     },
6416      /**
6417      * Remove all rows
6418      */
6419     clear : function()
6420     {
6421         this.el.select('tbody', true).first().dom.innerHTML = '';
6422     },
6423     /**
6424      * Show or hide a row.
6425      * @param {Number} rowIndex to show or hide
6426      * @param {Boolean} state hide
6427      */
6428     setRowVisibility : function(rowIndex, state)
6429     {
6430         var bt = this.mainBody.dom;
6431         
6432         var rows = this.el.select('tbody > tr', true).elements;
6433         
6434         if(typeof(rows[rowIndex]) == 'undefined'){
6435             return;
6436         }
6437         rows[rowIndex].dom.style.display = state ? '' : 'none';
6438     },
6439     
6440     
6441     getSelectionModel : function(){
6442         if(!this.selModel){
6443             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6444         }
6445         return this.selModel;
6446     },
6447     /*
6448      * Render the Roo.bootstrap object from renderder
6449      */
6450     renderCellObject : function(r)
6451     {
6452         var _this = this;
6453         
6454         var t = r.cfg.render(r.container);
6455         
6456         if(r.cfg.cn){
6457             Roo.each(r.cfg.cn, function(c){
6458                 var child = {
6459                     container: t.getChildContainer(),
6460                     cfg: c
6461                 };
6462                 _this.renderCellObject(child);
6463             })
6464         }
6465     },
6466     
6467     getRowIndex : function(row)
6468     {
6469         var rowIndex = -1;
6470         
6471         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6472             if(el != row){
6473                 return;
6474             }
6475             
6476             rowIndex = index;
6477         });
6478         
6479         return rowIndex;
6480     }
6481    
6482 });
6483
6484  
6485
6486  /*
6487  * - LGPL
6488  *
6489  * table cell
6490  * 
6491  */
6492
6493 /**
6494  * @class Roo.bootstrap.TableCell
6495  * @extends Roo.bootstrap.Component
6496  * Bootstrap TableCell class
6497  * @cfg {String} html cell contain text
6498  * @cfg {String} cls cell class
6499  * @cfg {String} tag cell tag (td|th) default td
6500  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6501  * @cfg {String} align Aligns the content in a cell
6502  * @cfg {String} axis Categorizes cells
6503  * @cfg {String} bgcolor Specifies the background color of a cell
6504  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6505  * @cfg {Number} colspan Specifies the number of columns a cell should span
6506  * @cfg {String} headers Specifies one or more header cells a cell is related to
6507  * @cfg {Number} height Sets the height of a cell
6508  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6509  * @cfg {Number} rowspan Sets the number of rows a cell should span
6510  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6511  * @cfg {String} valign Vertical aligns the content in a cell
6512  * @cfg {Number} width Specifies the width of a cell
6513  * 
6514  * @constructor
6515  * Create a new TableCell
6516  * @param {Object} config The config object
6517  */
6518
6519 Roo.bootstrap.TableCell = function(config){
6520     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6521 };
6522
6523 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6524     
6525     html: false,
6526     cls: false,
6527     tag: false,
6528     abbr: false,
6529     align: false,
6530     axis: false,
6531     bgcolor: false,
6532     charoff: false,
6533     colspan: false,
6534     headers: false,
6535     height: false,
6536     nowrap: false,
6537     rowspan: false,
6538     scope: false,
6539     valign: false,
6540     width: false,
6541     
6542     
6543     getAutoCreate : function(){
6544         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6545         
6546         cfg = {
6547             tag: 'td'
6548         };
6549         
6550         if(this.tag){
6551             cfg.tag = this.tag;
6552         }
6553         
6554         if (this.html) {
6555             cfg.html=this.html
6556         }
6557         if (this.cls) {
6558             cfg.cls=this.cls
6559         }
6560         if (this.abbr) {
6561             cfg.abbr=this.abbr
6562         }
6563         if (this.align) {
6564             cfg.align=this.align
6565         }
6566         if (this.axis) {
6567             cfg.axis=this.axis
6568         }
6569         if (this.bgcolor) {
6570             cfg.bgcolor=this.bgcolor
6571         }
6572         if (this.charoff) {
6573             cfg.charoff=this.charoff
6574         }
6575         if (this.colspan) {
6576             cfg.colspan=this.colspan
6577         }
6578         if (this.headers) {
6579             cfg.headers=this.headers
6580         }
6581         if (this.height) {
6582             cfg.height=this.height
6583         }
6584         if (this.nowrap) {
6585             cfg.nowrap=this.nowrap
6586         }
6587         if (this.rowspan) {
6588             cfg.rowspan=this.rowspan
6589         }
6590         if (this.scope) {
6591             cfg.scope=this.scope
6592         }
6593         if (this.valign) {
6594             cfg.valign=this.valign
6595         }
6596         if (this.width) {
6597             cfg.width=this.width
6598         }
6599         
6600         
6601         return cfg;
6602     }
6603    
6604 });
6605
6606  
6607
6608  /*
6609  * - LGPL
6610  *
6611  * table row
6612  * 
6613  */
6614
6615 /**
6616  * @class Roo.bootstrap.TableRow
6617  * @extends Roo.bootstrap.Component
6618  * Bootstrap TableRow class
6619  * @cfg {String} cls row class
6620  * @cfg {String} align Aligns the content in a table row
6621  * @cfg {String} bgcolor Specifies a background color for a table row
6622  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6623  * @cfg {String} valign Vertical aligns the content in a table row
6624  * 
6625  * @constructor
6626  * Create a new TableRow
6627  * @param {Object} config The config object
6628  */
6629
6630 Roo.bootstrap.TableRow = function(config){
6631     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6632 };
6633
6634 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6635     
6636     cls: false,
6637     align: false,
6638     bgcolor: false,
6639     charoff: false,
6640     valign: false,
6641     
6642     getAutoCreate : function(){
6643         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6644         
6645         cfg = {
6646             tag: 'tr'
6647         };
6648             
6649         if(this.cls){
6650             cfg.cls = this.cls;
6651         }
6652         if(this.align){
6653             cfg.align = this.align;
6654         }
6655         if(this.bgcolor){
6656             cfg.bgcolor = this.bgcolor;
6657         }
6658         if(this.charoff){
6659             cfg.charoff = this.charoff;
6660         }
6661         if(this.valign){
6662             cfg.valign = this.valign;
6663         }
6664         
6665         return cfg;
6666     }
6667    
6668 });
6669
6670  
6671
6672  /*
6673  * - LGPL
6674  *
6675  * table body
6676  * 
6677  */
6678
6679 /**
6680  * @class Roo.bootstrap.TableBody
6681  * @extends Roo.bootstrap.Component
6682  * Bootstrap TableBody class
6683  * @cfg {String} cls element class
6684  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6685  * @cfg {String} align Aligns the content inside the element
6686  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6687  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6688  * 
6689  * @constructor
6690  * Create a new TableBody
6691  * @param {Object} config The config object
6692  */
6693
6694 Roo.bootstrap.TableBody = function(config){
6695     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6696 };
6697
6698 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6699     
6700     cls: false,
6701     tag: false,
6702     align: false,
6703     charoff: false,
6704     valign: false,
6705     
6706     getAutoCreate : function(){
6707         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6708         
6709         cfg = {
6710             tag: 'tbody'
6711         };
6712             
6713         if (this.cls) {
6714             cfg.cls=this.cls
6715         }
6716         if(this.tag){
6717             cfg.tag = this.tag;
6718         }
6719         
6720         if(this.align){
6721             cfg.align = this.align;
6722         }
6723         if(this.charoff){
6724             cfg.charoff = this.charoff;
6725         }
6726         if(this.valign){
6727             cfg.valign = this.valign;
6728         }
6729         
6730         return cfg;
6731     }
6732     
6733     
6734 //    initEvents : function()
6735 //    {
6736 //        
6737 //        if(!this.store){
6738 //            return;
6739 //        }
6740 //        
6741 //        this.store = Roo.factory(this.store, Roo.data);
6742 //        this.store.on('load', this.onLoad, this);
6743 //        
6744 //        this.store.load();
6745 //        
6746 //    },
6747 //    
6748 //    onLoad: function () 
6749 //    {   
6750 //        this.fireEvent('load', this);
6751 //    }
6752 //    
6753 //   
6754 });
6755
6756  
6757
6758  /*
6759  * Based on:
6760  * Ext JS Library 1.1.1
6761  * Copyright(c) 2006-2007, Ext JS, LLC.
6762  *
6763  * Originally Released Under LGPL - original licence link has changed is not relivant.
6764  *
6765  * Fork - LGPL
6766  * <script type="text/javascript">
6767  */
6768
6769 // as we use this in bootstrap.
6770 Roo.namespace('Roo.form');
6771  /**
6772  * @class Roo.form.Action
6773  * Internal Class used to handle form actions
6774  * @constructor
6775  * @param {Roo.form.BasicForm} el The form element or its id
6776  * @param {Object} config Configuration options
6777  */
6778
6779  
6780  
6781 // define the action interface
6782 Roo.form.Action = function(form, options){
6783     this.form = form;
6784     this.options = options || {};
6785 };
6786 /**
6787  * Client Validation Failed
6788  * @const 
6789  */
6790 Roo.form.Action.CLIENT_INVALID = 'client';
6791 /**
6792  * Server Validation Failed
6793  * @const 
6794  */
6795 Roo.form.Action.SERVER_INVALID = 'server';
6796  /**
6797  * Connect to Server Failed
6798  * @const 
6799  */
6800 Roo.form.Action.CONNECT_FAILURE = 'connect';
6801 /**
6802  * Reading Data from Server Failed
6803  * @const 
6804  */
6805 Roo.form.Action.LOAD_FAILURE = 'load';
6806
6807 Roo.form.Action.prototype = {
6808     type : 'default',
6809     failureType : undefined,
6810     response : undefined,
6811     result : undefined,
6812
6813     // interface method
6814     run : function(options){
6815
6816     },
6817
6818     // interface method
6819     success : function(response){
6820
6821     },
6822
6823     // interface method
6824     handleResponse : function(response){
6825
6826     },
6827
6828     // default connection failure
6829     failure : function(response){
6830         
6831         this.response = response;
6832         this.failureType = Roo.form.Action.CONNECT_FAILURE;
6833         this.form.afterAction(this, false);
6834     },
6835
6836     processResponse : function(response){
6837         this.response = response;
6838         if(!response.responseText){
6839             return true;
6840         }
6841         this.result = this.handleResponse(response);
6842         return this.result;
6843     },
6844
6845     // utility functions used internally
6846     getUrl : function(appendParams){
6847         var url = this.options.url || this.form.url || this.form.el.dom.action;
6848         if(appendParams){
6849             var p = this.getParams();
6850             if(p){
6851                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
6852             }
6853         }
6854         return url;
6855     },
6856
6857     getMethod : function(){
6858         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
6859     },
6860
6861     getParams : function(){
6862         var bp = this.form.baseParams;
6863         var p = this.options.params;
6864         if(p){
6865             if(typeof p == "object"){
6866                 p = Roo.urlEncode(Roo.applyIf(p, bp));
6867             }else if(typeof p == 'string' && bp){
6868                 p += '&' + Roo.urlEncode(bp);
6869             }
6870         }else if(bp){
6871             p = Roo.urlEncode(bp);
6872         }
6873         return p;
6874     },
6875
6876     createCallback : function(){
6877         return {
6878             success: this.success,
6879             failure: this.failure,
6880             scope: this,
6881             timeout: (this.form.timeout*1000),
6882             upload: this.form.fileUpload ? this.success : undefined
6883         };
6884     }
6885 };
6886
6887 Roo.form.Action.Submit = function(form, options){
6888     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6889 };
6890
6891 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6892     type : 'submit',
6893
6894     haveProgress : false,
6895     uploadComplete : false,
6896     
6897     // uploadProgress indicator.
6898     uploadProgress : function()
6899     {
6900         if (!this.form.progressUrl) {
6901             return;
6902         }
6903         
6904         if (!this.haveProgress) {
6905             Roo.MessageBox.progress("Uploading", "Uploading");
6906         }
6907         if (this.uploadComplete) {
6908            Roo.MessageBox.hide();
6909            return;
6910         }
6911         
6912         this.haveProgress = true;
6913    
6914         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6915         
6916         var c = new Roo.data.Connection();
6917         c.request({
6918             url : this.form.progressUrl,
6919             params: {
6920                 id : uid
6921             },
6922             method: 'GET',
6923             success : function(req){
6924                //console.log(data);
6925                 var rdata = false;
6926                 var edata;
6927                 try  {
6928                    rdata = Roo.decode(req.responseText)
6929                 } catch (e) {
6930                     Roo.log("Invalid data from server..");
6931                     Roo.log(edata);
6932                     return;
6933                 }
6934                 if (!rdata || !rdata.success) {
6935                     Roo.log(rdata);
6936                     Roo.MessageBox.alert(Roo.encode(rdata));
6937                     return;
6938                 }
6939                 var data = rdata.data;
6940                 
6941                 if (this.uploadComplete) {
6942                    Roo.MessageBox.hide();
6943                    return;
6944                 }
6945                    
6946                 if (data){
6947                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6948                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6949                     );
6950                 }
6951                 this.uploadProgress.defer(2000,this);
6952             },
6953        
6954             failure: function(data) {
6955                 Roo.log('progress url failed ');
6956                 Roo.log(data);
6957             },
6958             scope : this
6959         });
6960            
6961     },
6962     
6963     
6964     run : function()
6965     {
6966         // run get Values on the form, so it syncs any secondary forms.
6967         this.form.getValues();
6968         
6969         var o = this.options;
6970         var method = this.getMethod();
6971         var isPost = method == 'POST';
6972         if(o.clientValidation === false || this.form.isValid()){
6973             
6974             if (this.form.progressUrl) {
6975                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6976                     (new Date() * 1) + '' + Math.random());
6977                     
6978             } 
6979             
6980             
6981             Roo.Ajax.request(Roo.apply(this.createCallback(), {
6982                 form:this.form.el.dom,
6983                 url:this.getUrl(!isPost),
6984                 method: method,
6985                 params:isPost ? this.getParams() : null,
6986                 isUpload: this.form.fileUpload
6987             }));
6988             
6989             this.uploadProgress();
6990
6991         }else if (o.clientValidation !== false){ // client validation failed
6992             this.failureType = Roo.form.Action.CLIENT_INVALID;
6993             this.form.afterAction(this, false);
6994         }
6995     },
6996
6997     success : function(response)
6998     {
6999         this.uploadComplete= true;
7000         if (this.haveProgress) {
7001             Roo.MessageBox.hide();
7002         }
7003         
7004         
7005         var result = this.processResponse(response);
7006         if(result === true || result.success){
7007             this.form.afterAction(this, true);
7008             return;
7009         }
7010         if(result.errors){
7011             this.form.markInvalid(result.errors);
7012             this.failureType = Roo.form.Action.SERVER_INVALID;
7013         }
7014         this.form.afterAction(this, false);
7015     },
7016     failure : function(response)
7017     {
7018         this.uploadComplete= true;
7019         if (this.haveProgress) {
7020             Roo.MessageBox.hide();
7021         }
7022         
7023         this.response = response;
7024         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7025         this.form.afterAction(this, false);
7026     },
7027     
7028     handleResponse : function(response){
7029         if(this.form.errorReader){
7030             var rs = this.form.errorReader.read(response);
7031             var errors = [];
7032             if(rs.records){
7033                 for(var i = 0, len = rs.records.length; i < len; i++) {
7034                     var r = rs.records[i];
7035                     errors[i] = r.data;
7036                 }
7037             }
7038             if(errors.length < 1){
7039                 errors = null;
7040             }
7041             return {
7042                 success : rs.success,
7043                 errors : errors
7044             };
7045         }
7046         var ret = false;
7047         try {
7048             ret = Roo.decode(response.responseText);
7049         } catch (e) {
7050             ret = {
7051                 success: false,
7052                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7053                 errors : []
7054             };
7055         }
7056         return ret;
7057         
7058     }
7059 });
7060
7061
7062 Roo.form.Action.Load = function(form, options){
7063     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7064     this.reader = this.form.reader;
7065 };
7066
7067 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7068     type : 'load',
7069
7070     run : function(){
7071         
7072         Roo.Ajax.request(Roo.apply(
7073                 this.createCallback(), {
7074                     method:this.getMethod(),
7075                     url:this.getUrl(false),
7076                     params:this.getParams()
7077         }));
7078     },
7079
7080     success : function(response){
7081         
7082         var result = this.processResponse(response);
7083         if(result === true || !result.success || !result.data){
7084             this.failureType = Roo.form.Action.LOAD_FAILURE;
7085             this.form.afterAction(this, false);
7086             return;
7087         }
7088         this.form.clearInvalid();
7089         this.form.setValues(result.data);
7090         this.form.afterAction(this, true);
7091     },
7092
7093     handleResponse : function(response){
7094         if(this.form.reader){
7095             var rs = this.form.reader.read(response);
7096             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7097             return {
7098                 success : rs.success,
7099                 data : data
7100             };
7101         }
7102         return Roo.decode(response.responseText);
7103     }
7104 });
7105
7106 Roo.form.Action.ACTION_TYPES = {
7107     'load' : Roo.form.Action.Load,
7108     'submit' : Roo.form.Action.Submit
7109 };/*
7110  * - LGPL
7111  *
7112  * form
7113  * 
7114  */
7115
7116 /**
7117  * @class Roo.bootstrap.Form
7118  * @extends Roo.bootstrap.Component
7119  * Bootstrap Form class
7120  * @cfg {String} method  GET | POST (default POST)
7121  * @cfg {String} labelAlign top | left (default top)
7122  * @cfg {String} align left  | right - for navbars
7123  * @cfg {Boolean} loadMask load mask when submit (default true)
7124
7125  * 
7126  * @constructor
7127  * Create a new Form
7128  * @param {Object} config The config object
7129  */
7130
7131
7132 Roo.bootstrap.Form = function(config){
7133     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7134     this.addEvents({
7135         /**
7136          * @event clientvalidation
7137          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7138          * @param {Form} this
7139          * @param {Boolean} valid true if the form has passed client-side validation
7140          */
7141         clientvalidation: true,
7142         /**
7143          * @event beforeaction
7144          * Fires before any action is performed. Return false to cancel the action.
7145          * @param {Form} this
7146          * @param {Action} action The action to be performed
7147          */
7148         beforeaction: true,
7149         /**
7150          * @event actionfailed
7151          * Fires when an action fails.
7152          * @param {Form} this
7153          * @param {Action} action The action that failed
7154          */
7155         actionfailed : true,
7156         /**
7157          * @event actioncomplete
7158          * Fires when an action is completed.
7159          * @param {Form} this
7160          * @param {Action} action The action that completed
7161          */
7162         actioncomplete : true
7163     });
7164     
7165 };
7166
7167 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7168       
7169      /**
7170      * @cfg {String} method
7171      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7172      */
7173     method : 'POST',
7174     /**
7175      * @cfg {String} url
7176      * The URL to use for form actions if one isn't supplied in the action options.
7177      */
7178     /**
7179      * @cfg {Boolean} fileUpload
7180      * Set to true if this form is a file upload.
7181      */
7182      
7183     /**
7184      * @cfg {Object} baseParams
7185      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7186      */
7187       
7188     /**
7189      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7190      */
7191     timeout: 30,
7192     /**
7193      * @cfg {Sting} align (left|right) for navbar forms
7194      */
7195     align : 'left',
7196
7197     // private
7198     activeAction : null,
7199  
7200     /**
7201      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7202      * element by passing it or its id or mask the form itself by passing in true.
7203      * @type Mixed
7204      */
7205     waitMsgTarget : false,
7206     
7207     loadMask : true,
7208     
7209     getAutoCreate : function(){
7210         
7211         var cfg = {
7212             tag: 'form',
7213             method : this.method || 'POST',
7214             id : this.id || Roo.id(),
7215             cls : ''
7216         };
7217         if (this.parent().xtype.match(/^Nav/)) {
7218             cfg.cls = 'navbar-form navbar-' + this.align;
7219             
7220         }
7221         
7222         if (this.labelAlign == 'left' ) {
7223             cfg.cls += ' form-horizontal';
7224         }
7225         
7226         
7227         return cfg;
7228     },
7229     initEvents : function()
7230     {
7231         this.el.on('submit', this.onSubmit, this);
7232         // this was added as random key presses on the form where triggering form submit.
7233         this.el.on('keypress', function(e) {
7234             if (e.getCharCode() != 13) {
7235                 return true;
7236             }
7237             // we might need to allow it for textareas.. and some other items.
7238             // check e.getTarget().
7239             
7240             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7241                 return true;
7242             }
7243         
7244             Roo.log("keypress blocked");
7245             
7246             e.preventDefault();
7247             return false;
7248         });
7249         
7250     },
7251     // private
7252     onSubmit : function(e){
7253         e.stopEvent();
7254     },
7255     
7256      /**
7257      * Returns true if client-side validation on the form is successful.
7258      * @return Boolean
7259      */
7260     isValid : function(){
7261         var items = this.getItems();
7262         var valid = true;
7263         items.each(function(f){
7264            if(!f.validate()){
7265                valid = false;
7266                
7267            }
7268         });
7269         return valid;
7270     },
7271     /**
7272      * Returns true if any fields in this form have changed since their original load.
7273      * @return Boolean
7274      */
7275     isDirty : function(){
7276         var dirty = false;
7277         var items = this.getItems();
7278         items.each(function(f){
7279            if(f.isDirty()){
7280                dirty = true;
7281                return false;
7282            }
7283            return true;
7284         });
7285         return dirty;
7286     },
7287      /**
7288      * Performs a predefined action (submit or load) or custom actions you define on this form.
7289      * @param {String} actionName The name of the action type
7290      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7291      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7292      * accept other config options):
7293      * <pre>
7294 Property          Type             Description
7295 ----------------  ---------------  ----------------------------------------------------------------------------------
7296 url               String           The url for the action (defaults to the form's url)
7297 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7298 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7299 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7300                                    validate the form on the client (defaults to false)
7301      * </pre>
7302      * @return {BasicForm} this
7303      */
7304     doAction : function(action, options){
7305         if(typeof action == 'string'){
7306             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7307         }
7308         if(this.fireEvent('beforeaction', this, action) !== false){
7309             this.beforeAction(action);
7310             action.run.defer(100, action);
7311         }
7312         return this;
7313     },
7314     
7315     // private
7316     beforeAction : function(action){
7317         var o = action.options;
7318         
7319         if(this.loadMask){
7320             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7321         }
7322         // not really supported yet.. ??
7323         
7324         //if(this.waitMsgTarget === true){
7325         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7326         //}else if(this.waitMsgTarget){
7327         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7328         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7329         //}else {
7330         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7331        // }
7332          
7333     },
7334
7335     // private
7336     afterAction : function(action, success){
7337         this.activeAction = null;
7338         var o = action.options;
7339         
7340         //if(this.waitMsgTarget === true){
7341             this.el.unmask();
7342         //}else if(this.waitMsgTarget){
7343         //    this.waitMsgTarget.unmask();
7344         //}else{
7345         //    Roo.MessageBox.updateProgress(1);
7346         //    Roo.MessageBox.hide();
7347        // }
7348         // 
7349         if(success){
7350             if(o.reset){
7351                 this.reset();
7352             }
7353             Roo.callback(o.success, o.scope, [this, action]);
7354             this.fireEvent('actioncomplete', this, action);
7355             
7356         }else{
7357             
7358             // failure condition..
7359             // we have a scenario where updates need confirming.
7360             // eg. if a locking scenario exists..
7361             // we look for { errors : { needs_confirm : true }} in the response.
7362             if (
7363                 (typeof(action.result) != 'undefined')  &&
7364                 (typeof(action.result.errors) != 'undefined')  &&
7365                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7366            ){
7367                 var _t = this;
7368                 Roo.log("not supported yet");
7369                  /*
7370                 
7371                 Roo.MessageBox.confirm(
7372                     "Change requires confirmation",
7373                     action.result.errorMsg,
7374                     function(r) {
7375                         if (r != 'yes') {
7376                             return;
7377                         }
7378                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7379                     }
7380                     
7381                 );
7382                 */
7383                 
7384                 
7385                 return;
7386             }
7387             
7388             Roo.callback(o.failure, o.scope, [this, action]);
7389             // show an error message if no failed handler is set..
7390             if (!this.hasListener('actionfailed')) {
7391                 Roo.log("need to add dialog support");
7392                 /*
7393                 Roo.MessageBox.alert("Error",
7394                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7395                         action.result.errorMsg :
7396                         "Saving Failed, please check your entries or try again"
7397                 );
7398                 */
7399             }
7400             
7401             this.fireEvent('actionfailed', this, action);
7402         }
7403         
7404     },
7405     /**
7406      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7407      * @param {String} id The value to search for
7408      * @return Field
7409      */
7410     findField : function(id){
7411         var items = this.getItems();
7412         var field = items.get(id);
7413         if(!field){
7414              items.each(function(f){
7415                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7416                     field = f;
7417                     return false;
7418                 }
7419                 return true;
7420             });
7421         }
7422         return field || null;
7423     },
7424      /**
7425      * Mark fields in this form invalid in bulk.
7426      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7427      * @return {BasicForm} this
7428      */
7429     markInvalid : function(errors){
7430         if(errors instanceof Array){
7431             for(var i = 0, len = errors.length; i < len; i++){
7432                 var fieldError = errors[i];
7433                 var f = this.findField(fieldError.id);
7434                 if(f){
7435                     f.markInvalid(fieldError.msg);
7436                 }
7437             }
7438         }else{
7439             var field, id;
7440             for(id in errors){
7441                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7442                     field.markInvalid(errors[id]);
7443                 }
7444             }
7445         }
7446         //Roo.each(this.childForms || [], function (f) {
7447         //    f.markInvalid(errors);
7448         //});
7449         
7450         return this;
7451     },
7452
7453     /**
7454      * Set values for fields in this form in bulk.
7455      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7456      * @return {BasicForm} this
7457      */
7458     setValues : function(values){
7459         if(values instanceof Array){ // array of objects
7460             for(var i = 0, len = values.length; i < len; i++){
7461                 var v = values[i];
7462                 var f = this.findField(v.id);
7463                 if(f){
7464                     f.setValue(v.value);
7465                     if(this.trackResetOnLoad){
7466                         f.originalValue = f.getValue();
7467                     }
7468                 }
7469             }
7470         }else{ // object hash
7471             var field, id;
7472             for(id in values){
7473                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7474                     
7475                     if (field.setFromData && 
7476                         field.valueField && 
7477                         field.displayField &&
7478                         // combos' with local stores can 
7479                         // be queried via setValue()
7480                         // to set their value..
7481                         (field.store && !field.store.isLocal)
7482                         ) {
7483                         // it's a combo
7484                         var sd = { };
7485                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7486                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7487                         field.setFromData(sd);
7488                         
7489                     } else {
7490                         field.setValue(values[id]);
7491                     }
7492                     
7493                     
7494                     if(this.trackResetOnLoad){
7495                         field.originalValue = field.getValue();
7496                     }
7497                 }
7498             }
7499         }
7500          
7501         //Roo.each(this.childForms || [], function (f) {
7502         //    f.setValues(values);
7503         //});
7504                 
7505         return this;
7506     },
7507
7508     /**
7509      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7510      * they are returned as an array.
7511      * @param {Boolean} asString
7512      * @return {Object}
7513      */
7514     getValues : function(asString){
7515         //if (this.childForms) {
7516             // copy values from the child forms
7517         //    Roo.each(this.childForms, function (f) {
7518         //        this.setValues(f.getValues());
7519         //    }, this);
7520         //}
7521         
7522         
7523         
7524         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7525         if(asString === true){
7526             return fs;
7527         }
7528         return Roo.urlDecode(fs);
7529     },
7530     
7531     /**
7532      * Returns the fields in this form as an object with key/value pairs. 
7533      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7534      * @return {Object}
7535      */
7536     getFieldValues : function(with_hidden)
7537     {
7538         var items = this.getItems();
7539         var ret = {};
7540         items.each(function(f){
7541             if (!f.getName()) {
7542                 return;
7543             }
7544             var v = f.getValue();
7545             if (f.inputType =='radio') {
7546                 if (typeof(ret[f.getName()]) == 'undefined') {
7547                     ret[f.getName()] = ''; // empty..
7548                 }
7549                 
7550                 if (!f.el.dom.checked) {
7551                     return;
7552                     
7553                 }
7554                 v = f.el.dom.value;
7555                 
7556             }
7557             
7558             // not sure if this supported any more..
7559             if ((typeof(v) == 'object') && f.getRawValue) {
7560                 v = f.getRawValue() ; // dates..
7561             }
7562             // combo boxes where name != hiddenName...
7563             if (f.name != f.getName()) {
7564                 ret[f.name] = f.getRawValue();
7565             }
7566             ret[f.getName()] = v;
7567         });
7568         
7569         return ret;
7570     },
7571
7572     /**
7573      * Clears all invalid messages in this form.
7574      * @return {BasicForm} this
7575      */
7576     clearInvalid : function(){
7577         var items = this.getItems();
7578         
7579         items.each(function(f){
7580            f.clearInvalid();
7581         });
7582         
7583         
7584         
7585         return this;
7586     },
7587
7588     /**
7589      * Resets this form.
7590      * @return {BasicForm} this
7591      */
7592     reset : function(){
7593         var items = this.getItems();
7594         items.each(function(f){
7595             f.reset();
7596         });
7597         
7598         Roo.each(this.childForms || [], function (f) {
7599             f.reset();
7600         });
7601        
7602         
7603         return this;
7604     },
7605     getItems : function()
7606     {
7607         var r=new Roo.util.MixedCollection(false, function(o){
7608             return o.id || (o.id = Roo.id());
7609         });
7610         var iter = function(el) {
7611             if (el.inputEl) {
7612                 r.add(el);
7613             }
7614             if (!el.items) {
7615                 return;
7616             }
7617             Roo.each(el.items,function(e) {
7618                 iter(e);
7619             });
7620             
7621             
7622         };
7623         
7624         iter(this);
7625         return r;
7626         
7627         
7628         
7629         
7630     }
7631     
7632 });
7633
7634  
7635 /*
7636  * Based on:
7637  * Ext JS Library 1.1.1
7638  * Copyright(c) 2006-2007, Ext JS, LLC.
7639  *
7640  * Originally Released Under LGPL - original licence link has changed is not relivant.
7641  *
7642  * Fork - LGPL
7643  * <script type="text/javascript">
7644  */
7645 /**
7646  * @class Roo.form.VTypes
7647  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7648  * @singleton
7649  */
7650 Roo.form.VTypes = function(){
7651     // closure these in so they are only created once.
7652     var alpha = /^[a-zA-Z_]+$/;
7653     var alphanum = /^[a-zA-Z0-9_]+$/;
7654     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7655     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7656
7657     // All these messages and functions are configurable
7658     return {
7659         /**
7660          * The function used to validate email addresses
7661          * @param {String} value The email address
7662          */
7663         'email' : function(v){
7664             return email.test(v);
7665         },
7666         /**
7667          * The error text to display when the email validation function returns false
7668          * @type String
7669          */
7670         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7671         /**
7672          * The keystroke filter mask to be applied on email input
7673          * @type RegExp
7674          */
7675         'emailMask' : /[a-z0-9_\.\-@]/i,
7676
7677         /**
7678          * The function used to validate URLs
7679          * @param {String} value The URL
7680          */
7681         'url' : function(v){
7682             return url.test(v);
7683         },
7684         /**
7685          * The error text to display when the url validation function returns false
7686          * @type String
7687          */
7688         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7689         
7690         /**
7691          * The function used to validate alpha values
7692          * @param {String} value The value
7693          */
7694         'alpha' : function(v){
7695             return alpha.test(v);
7696         },
7697         /**
7698          * The error text to display when the alpha validation function returns false
7699          * @type String
7700          */
7701         'alphaText' : 'This field should only contain letters and _',
7702         /**
7703          * The keystroke filter mask to be applied on alpha input
7704          * @type RegExp
7705          */
7706         'alphaMask' : /[a-z_]/i,
7707
7708         /**
7709          * The function used to validate alphanumeric values
7710          * @param {String} value The value
7711          */
7712         'alphanum' : function(v){
7713             return alphanum.test(v);
7714         },
7715         /**
7716          * The error text to display when the alphanumeric validation function returns false
7717          * @type String
7718          */
7719         'alphanumText' : 'This field should only contain letters, numbers and _',
7720         /**
7721          * The keystroke filter mask to be applied on alphanumeric input
7722          * @type RegExp
7723          */
7724         'alphanumMask' : /[a-z0-9_]/i
7725     };
7726 }();/*
7727  * - LGPL
7728  *
7729  * Input
7730  * 
7731  */
7732
7733 /**
7734  * @class Roo.bootstrap.Input
7735  * @extends Roo.bootstrap.Component
7736  * Bootstrap Input class
7737  * @cfg {Boolean} disabled is it disabled
7738  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7739  * @cfg {String} name name of the input
7740  * @cfg {string} fieldLabel - the label associated
7741  * @cfg {string} placeholder - placeholder to put in text.
7742  * @cfg {string}  before - input group add on before
7743  * @cfg {string} after - input group add on after
7744  * @cfg {string} size - (lg|sm) or leave empty..
7745  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
7746  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
7747  * @cfg {Number} md colspan out of 12 for computer-sized screens
7748  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
7749  * @cfg {string} value default value of the input
7750  * @cfg {Number} labelWidth set the width of label (0-12)
7751  * @cfg {String} labelAlign (top|left)
7752  * @cfg {Boolean} readOnly Specifies that the field should be read-only
7753  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
7754
7755  * @cfg {String} align (left|center|right) Default left
7756  * @cfg {Boolean} forceFeedback (true|false) Default false
7757  * 
7758  * 
7759  * 
7760  * 
7761  * @constructor
7762  * Create a new Input
7763  * @param {Object} config The config object
7764  */
7765
7766 Roo.bootstrap.Input = function(config){
7767     Roo.bootstrap.Input.superclass.constructor.call(this, config);
7768    
7769         this.addEvents({
7770             /**
7771              * @event focus
7772              * Fires when this field receives input focus.
7773              * @param {Roo.form.Field} this
7774              */
7775             focus : true,
7776             /**
7777              * @event blur
7778              * Fires when this field loses input focus.
7779              * @param {Roo.form.Field} this
7780              */
7781             blur : true,
7782             /**
7783              * @event specialkey
7784              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
7785              * {@link Roo.EventObject#getKey} to determine which key was pressed.
7786              * @param {Roo.form.Field} this
7787              * @param {Roo.EventObject} e The event object
7788              */
7789             specialkey : true,
7790             /**
7791              * @event change
7792              * Fires just before the field blurs if the field value has changed.
7793              * @param {Roo.form.Field} this
7794              * @param {Mixed} newValue The new value
7795              * @param {Mixed} oldValue The original value
7796              */
7797             change : true,
7798             /**
7799              * @event invalid
7800              * Fires after the field has been marked as invalid.
7801              * @param {Roo.form.Field} this
7802              * @param {String} msg The validation message
7803              */
7804             invalid : true,
7805             /**
7806              * @event valid
7807              * Fires after the field has been validated with no errors.
7808              * @param {Roo.form.Field} this
7809              */
7810             valid : true,
7811              /**
7812              * @event keyup
7813              * Fires after the key up
7814              * @param {Roo.form.Field} this
7815              * @param {Roo.EventObject}  e The event Object
7816              */
7817             keyup : true
7818         });
7819 };
7820
7821 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
7822      /**
7823      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
7824       automatic validation (defaults to "keyup").
7825      */
7826     validationEvent : "keyup",
7827      /**
7828      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
7829      */
7830     validateOnBlur : true,
7831     /**
7832      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
7833      */
7834     validationDelay : 250,
7835      /**
7836      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
7837      */
7838     focusClass : "x-form-focus",  // not needed???
7839     
7840        
7841     /**
7842      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
7843      */
7844     invalidClass : "has-warning",
7845     
7846     /**
7847      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
7848      */
7849     validClass : "has-success",
7850     
7851     /**
7852      * @cfg {Boolean} hasFeedback (true|false) default true
7853      */
7854     hasFeedback : true,
7855     
7856     /**
7857      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7858      */
7859     invalidFeedbackClass : "glyphicon-warning-sign",
7860     
7861     /**
7862      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
7863      */
7864     validFeedbackClass : "glyphicon-ok",
7865     
7866     /**
7867      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
7868      */
7869     selectOnFocus : false,
7870     
7871      /**
7872      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
7873      */
7874     maskRe : null,
7875        /**
7876      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
7877      */
7878     vtype : null,
7879     
7880       /**
7881      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
7882      */
7883     disableKeyFilter : false,
7884     
7885        /**
7886      * @cfg {Boolean} disabled True to disable the field (defaults to false).
7887      */
7888     disabled : false,
7889      /**
7890      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7891      */
7892     allowBlank : true,
7893     /**
7894      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7895      */
7896     blankText : "This field is required",
7897     
7898      /**
7899      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7900      */
7901     minLength : 0,
7902     /**
7903      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7904      */
7905     maxLength : Number.MAX_VALUE,
7906     /**
7907      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7908      */
7909     minLengthText : "The minimum length for this field is {0}",
7910     /**
7911      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7912      */
7913     maxLengthText : "The maximum length for this field is {0}",
7914   
7915     
7916     /**
7917      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7918      * If available, this function will be called only after the basic validators all return true, and will be passed the
7919      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7920      */
7921     validator : null,
7922     /**
7923      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7924      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7925      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
7926      */
7927     regex : null,
7928     /**
7929      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7930      */
7931     regexText : "",
7932     
7933     autocomplete: false,
7934     
7935     
7936     fieldLabel : '',
7937     inputType : 'text',
7938     
7939     name : false,
7940     placeholder: false,
7941     before : false,
7942     after : false,
7943     size : false,
7944     hasFocus : false,
7945     preventMark: false,
7946     isFormField : true,
7947     value : '',
7948     labelWidth : 2,
7949     labelAlign : false,
7950     readOnly : false,
7951     align : false,
7952     formatedValue : false,
7953     forceFeedback : false,
7954     
7955     parentLabelAlign : function()
7956     {
7957         var parent = this;
7958         while (parent.parent()) {
7959             parent = parent.parent();
7960             if (typeof(parent.labelAlign) !='undefined') {
7961                 return parent.labelAlign;
7962             }
7963         }
7964         return 'left';
7965         
7966     },
7967     
7968     getAutoCreate : function(){
7969         
7970         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7971         
7972         var id = Roo.id();
7973         
7974         var cfg = {};
7975         
7976         if(this.inputType != 'hidden'){
7977             cfg.cls = 'form-group' //input-group
7978         }
7979         
7980         var input =  {
7981             tag: 'input',
7982             id : id,
7983             type : this.inputType,
7984             value : this.value,
7985             cls : 'form-control',
7986             placeholder : this.placeholder || '',
7987             autocomplete : this.autocomplete || 'new-password'
7988         };
7989         
7990         
7991         if(this.align){
7992             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7993         }
7994         
7995         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7996             input.maxLength = this.maxLength;
7997         }
7998         
7999         if (this.disabled) {
8000             input.disabled=true;
8001         }
8002         
8003         if (this.readOnly) {
8004             input.readonly=true;
8005         }
8006         
8007         if (this.name) {
8008             input.name = this.name;
8009         }
8010         if (this.size) {
8011             input.cls += ' input-' + this.size;
8012         }
8013         var settings=this;
8014         ['xs','sm','md','lg'].map(function(size){
8015             if (settings[size]) {
8016                 cfg.cls += ' col-' + size + '-' + settings[size];
8017             }
8018         });
8019         
8020         var inputblock = input;
8021         
8022         var feedback = {
8023             tag: 'span',
8024             cls: 'glyphicon form-control-feedback'
8025         };
8026             
8027         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8028             
8029             inputblock = {
8030                 cls : 'has-feedback',
8031                 cn :  [
8032                     input,
8033                     feedback
8034                 ] 
8035             };  
8036         }
8037         
8038         if (this.before || this.after) {
8039             
8040             inputblock = {
8041                 cls : 'input-group',
8042                 cn :  [] 
8043             };
8044             
8045             if (this.before && typeof(this.before) == 'string') {
8046                 
8047                 inputblock.cn.push({
8048                     tag :'span',
8049                     cls : 'roo-input-before input-group-addon',
8050                     html : this.before
8051                 });
8052             }
8053             if (this.before && typeof(this.before) == 'object') {
8054                 this.before = Roo.factory(this.before);
8055                 
8056                 inputblock.cn.push({
8057                     tag :'span',
8058                     cls : 'roo-input-before input-group-' +
8059                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8060                 });
8061             }
8062             
8063             inputblock.cn.push(input);
8064             
8065             if (this.after && typeof(this.after) == 'string') {
8066                 inputblock.cn.push({
8067                     tag :'span',
8068                     cls : 'roo-input-after input-group-addon',
8069                     html : this.after
8070                 });
8071             }
8072             if (this.after && typeof(this.after) == 'object') {
8073                 this.after = Roo.factory(this.after);
8074                 
8075                 inputblock.cn.push({
8076                     tag :'span',
8077                     cls : 'roo-input-after input-group-' +
8078                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8079                 });
8080             }
8081             
8082             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8083                 inputblock.cls += ' has-feedback';
8084                 inputblock.cn.push(feedback);
8085             }
8086         };
8087         
8088         if (align ==='left' && this.fieldLabel.length) {
8089                 
8090                 cfg.cn = [
8091                     
8092                     {
8093                         tag: 'label',
8094                         'for' :  id,
8095                         cls : 'control-label col-sm-' + this.labelWidth,
8096                         html : this.fieldLabel
8097                         
8098                     },
8099                     {
8100                         cls : "col-sm-" + (12 - this.labelWidth), 
8101                         cn: [
8102                             inputblock
8103                         ]
8104                     }
8105                     
8106                 ];
8107         } else if ( this.fieldLabel.length) {
8108                 
8109                  cfg.cn = [
8110                    
8111                     {
8112                         tag: 'label',
8113                         //cls : 'input-group-addon',
8114                         html : this.fieldLabel
8115                         
8116                     },
8117                     
8118                     inputblock
8119                     
8120                 ];
8121
8122         } else {
8123             
8124                 cfg.cn = [
8125                     
8126                         inputblock
8127                     
8128                 ];
8129                 
8130                 
8131         };
8132         
8133         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8134            cfg.cls += ' navbar-form';
8135         }
8136         
8137         return cfg;
8138         
8139     },
8140     /**
8141      * return the real input element.
8142      */
8143     inputEl: function ()
8144     {
8145         return this.el.select('input.form-control',true).first();
8146     },
8147     
8148     tooltipEl : function()
8149     {
8150         return this.inputEl();
8151     },
8152     
8153     setDisabled : function(v)
8154     {
8155         var i  = this.inputEl().dom;
8156         if (!v) {
8157             i.removeAttribute('disabled');
8158             return;
8159             
8160         }
8161         i.setAttribute('disabled','true');
8162     },
8163     initEvents : function()
8164     {
8165           
8166         this.inputEl().on("keydown" , this.fireKey,  this);
8167         this.inputEl().on("focus", this.onFocus,  this);
8168         this.inputEl().on("blur", this.onBlur,  this);
8169         
8170         this.inputEl().relayEvent('keyup', this);
8171  
8172         // reference to original value for reset
8173         this.originalValue = this.getValue();
8174         //Roo.form.TextField.superclass.initEvents.call(this);
8175         if(this.validationEvent == 'keyup'){
8176             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8177             this.inputEl().on('keyup', this.filterValidation, this);
8178         }
8179         else if(this.validationEvent !== false){
8180             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8181         }
8182         
8183         if(this.selectOnFocus){
8184             this.on("focus", this.preFocus, this);
8185             
8186         }
8187         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8188             this.inputEl().on("keypress", this.filterKeys, this);
8189         }
8190        /* if(this.grow){
8191             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8192             this.el.on("click", this.autoSize,  this);
8193         }
8194         */
8195         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8196             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8197         }
8198         
8199         if (typeof(this.before) == 'object') {
8200             this.before.render(this.el.select('.roo-input-before',true).first());
8201         }
8202         if (typeof(this.after) == 'object') {
8203             this.after.render(this.el.select('.roo-input-after',true).first());
8204         }
8205         
8206         
8207     },
8208     filterValidation : function(e){
8209         if(!e.isNavKeyPress()){
8210             this.validationTask.delay(this.validationDelay);
8211         }
8212     },
8213      /**
8214      * Validates the field value
8215      * @return {Boolean} True if the value is valid, else false
8216      */
8217     validate : function(){
8218         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8219         if(this.disabled || this.validateValue(this.getRawValue())){
8220             this.markValid();
8221             return true;
8222         }
8223         
8224         this.markInvalid();
8225         return false;
8226     },
8227     
8228     
8229     /**
8230      * Validates a value according to the field's validation rules and marks the field as invalid
8231      * if the validation fails
8232      * @param {Mixed} value The value to validate
8233      * @return {Boolean} True if the value is valid, else false
8234      */
8235     validateValue : function(value){
8236         if(value.length < 1)  { // if it's blank
8237             if(this.allowBlank){
8238                 return true;
8239             }
8240             return false;
8241         }
8242         
8243         if(value.length < this.minLength){
8244             return false;
8245         }
8246         if(value.length > this.maxLength){
8247             return false;
8248         }
8249         if(this.vtype){
8250             var vt = Roo.form.VTypes;
8251             if(!vt[this.vtype](value, this)){
8252                 return false;
8253             }
8254         }
8255         if(typeof this.validator == "function"){
8256             var msg = this.validator(value);
8257             if(msg !== true){
8258                 return false;
8259             }
8260         }
8261         
8262         if(this.regex && !this.regex.test(value)){
8263             return false;
8264         }
8265         
8266         return true;
8267     },
8268
8269     
8270     
8271      // private
8272     fireKey : function(e){
8273         //Roo.log('field ' + e.getKey());
8274         if(e.isNavKeyPress()){
8275             this.fireEvent("specialkey", this, e);
8276         }
8277     },
8278     focus : function (selectText){
8279         if(this.rendered){
8280             this.inputEl().focus();
8281             if(selectText === true){
8282                 this.inputEl().dom.select();
8283             }
8284         }
8285         return this;
8286     } ,
8287     
8288     onFocus : function(){
8289         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8290            // this.el.addClass(this.focusClass);
8291         }
8292         if(!this.hasFocus){
8293             this.hasFocus = true;
8294             this.startValue = this.getValue();
8295             this.fireEvent("focus", this);
8296         }
8297     },
8298     
8299     beforeBlur : Roo.emptyFn,
8300
8301     
8302     // private
8303     onBlur : function(){
8304         this.beforeBlur();
8305         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8306             //this.el.removeClass(this.focusClass);
8307         }
8308         this.hasFocus = false;
8309         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8310             this.validate();
8311         }
8312         var v = this.getValue();
8313         if(String(v) !== String(this.startValue)){
8314             this.fireEvent('change', this, v, this.startValue);
8315         }
8316         this.fireEvent("blur", this);
8317     },
8318     
8319     /**
8320      * Resets the current field value to the originally loaded value and clears any validation messages
8321      */
8322     reset : function(){
8323         this.setValue(this.originalValue);
8324         this.validate();
8325     },
8326      /**
8327      * Returns the name of the field
8328      * @return {Mixed} name The name field
8329      */
8330     getName: function(){
8331         return this.name;
8332     },
8333      /**
8334      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8335      * @return {Mixed} value The field value
8336      */
8337     getValue : function(){
8338         
8339         var v = this.inputEl().getValue();
8340         
8341         return v;
8342     },
8343     /**
8344      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8345      * @return {Mixed} value The field value
8346      */
8347     getRawValue : function(){
8348         var v = this.inputEl().getValue();
8349         
8350         return v;
8351     },
8352     
8353     /**
8354      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8355      * @param {Mixed} value The value to set
8356      */
8357     setRawValue : function(v){
8358         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8359     },
8360     
8361     selectText : function(start, end){
8362         var v = this.getRawValue();
8363         if(v.length > 0){
8364             start = start === undefined ? 0 : start;
8365             end = end === undefined ? v.length : end;
8366             var d = this.inputEl().dom;
8367             if(d.setSelectionRange){
8368                 d.setSelectionRange(start, end);
8369             }else if(d.createTextRange){
8370                 var range = d.createTextRange();
8371                 range.moveStart("character", start);
8372                 range.moveEnd("character", v.length-end);
8373                 range.select();
8374             }
8375         }
8376     },
8377     
8378     /**
8379      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8380      * @param {Mixed} value The value to set
8381      */
8382     setValue : function(v){
8383         this.value = v;
8384         if(this.rendered){
8385             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8386             this.validate();
8387         }
8388     },
8389     
8390     /*
8391     processValue : function(value){
8392         if(this.stripCharsRe){
8393             var newValue = value.replace(this.stripCharsRe, '');
8394             if(newValue !== value){
8395                 this.setRawValue(newValue);
8396                 return newValue;
8397             }
8398         }
8399         return value;
8400     },
8401   */
8402     preFocus : function(){
8403         
8404         if(this.selectOnFocus){
8405             this.inputEl().dom.select();
8406         }
8407     },
8408     filterKeys : function(e){
8409         var k = e.getKey();
8410         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8411             return;
8412         }
8413         var c = e.getCharCode(), cc = String.fromCharCode(c);
8414         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8415             return;
8416         }
8417         if(!this.maskRe.test(cc)){
8418             e.stopEvent();
8419         }
8420     },
8421      /**
8422      * Clear any invalid styles/messages for this field
8423      */
8424     clearInvalid : function(){
8425         
8426         if(!this.el || this.preventMark){ // not rendered
8427             return;
8428         }
8429         
8430         var label = this.el.select('label', true).first();
8431         var icon = this.el.select('i.fa-star', true).first();
8432         
8433         if(label && icon){
8434             icon.remove();
8435         }
8436         
8437         this.el.removeClass(this.invalidClass);
8438         
8439         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8440             
8441             var feedback = this.el.select('.form-control-feedback', true).first();
8442             
8443             if(feedback){
8444                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8445             }
8446             
8447         }
8448         
8449         this.fireEvent('valid', this);
8450     },
8451     
8452      /**
8453      * Mark this field as valid
8454      */
8455     markValid : function()
8456     {
8457         if(!this.el  || this.preventMark){ // not rendered
8458             return;
8459         }
8460         
8461         this.el.removeClass([this.invalidClass, this.validClass]);
8462         
8463         var feedback = this.el.select('.form-control-feedback', true).first();
8464             
8465         if(feedback){
8466             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8467         }
8468
8469         if(this.disabled || this.allowBlank){
8470             return;
8471         }
8472         
8473         var formGroup = this.el.findParent('.form-group', false, true);
8474         
8475         if(formGroup){
8476             
8477             var label = formGroup.select('label', true).first();
8478             var icon = formGroup.select('i.fa-star', true).first();
8479             
8480             if(label && icon){
8481                 icon.remove();
8482             }
8483         }
8484         
8485         this.el.addClass(this.validClass);
8486         
8487         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8488             
8489             var feedback = this.el.select('.form-control-feedback', true).first();
8490             
8491             if(feedback){
8492                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8493                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8494             }
8495             
8496         }
8497         
8498         this.fireEvent('valid', this);
8499     },
8500     
8501      /**
8502      * Mark this field as invalid
8503      * @param {String} msg The validation message
8504      */
8505     markInvalid : function(msg)
8506     {
8507         if(!this.el  || this.preventMark){ // not rendered
8508             return;
8509         }
8510         
8511         this.el.removeClass([this.invalidClass, this.validClass]);
8512         
8513         var feedback = this.el.select('.form-control-feedback', true).first();
8514             
8515         if(feedback){
8516             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8517         }
8518
8519         if(this.disabled || this.allowBlank){
8520             return;
8521         }
8522         
8523         var formGroup = this.el.findParent('.form-group', false, true);
8524         
8525         if(formGroup){
8526             var label = formGroup.select('label', true).first();
8527             var icon = formGroup.select('i.fa-star', true).first();
8528
8529             if(!this.getValue().length && label && !icon){
8530                 this.el.findParent('.form-group', false, true).createChild({
8531                     tag : 'i',
8532                     cls : 'text-danger fa fa-lg fa-star',
8533                     tooltip : 'This field is required',
8534                     style : 'margin-right:5px;'
8535                 }, label, true);
8536             }
8537         }
8538         
8539         
8540         this.el.addClass(this.invalidClass);
8541         
8542         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8543             
8544             var feedback = this.el.select('.form-control-feedback', true).first();
8545             
8546             if(feedback){
8547                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8548                 
8549                 if(this.getValue().length || this.forceFeedback){
8550                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8551                 }
8552                 
8553             }
8554             
8555         }
8556         
8557         this.fireEvent('invalid', this, msg);
8558     },
8559     // private
8560     SafariOnKeyDown : function(event)
8561     {
8562         // this is a workaround for a password hang bug on chrome/ webkit.
8563         
8564         var isSelectAll = false;
8565         
8566         if(this.inputEl().dom.selectionEnd > 0){
8567             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8568         }
8569         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8570             event.preventDefault();
8571             this.setValue('');
8572             return;
8573         }
8574         
8575         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8576             
8577             event.preventDefault();
8578             // this is very hacky as keydown always get's upper case.
8579             //
8580             var cc = String.fromCharCode(event.getCharCode());
8581             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8582             
8583         }
8584     },
8585     adjustWidth : function(tag, w){
8586         tag = tag.toLowerCase();
8587         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8588             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8589                 if(tag == 'input'){
8590                     return w + 2;
8591                 }
8592                 if(tag == 'textarea'){
8593                     return w-2;
8594                 }
8595             }else if(Roo.isOpera){
8596                 if(tag == 'input'){
8597                     return w + 2;
8598                 }
8599                 if(tag == 'textarea'){
8600                     return w-2;
8601                 }
8602             }
8603         }
8604         return w;
8605     }
8606     
8607 });
8608
8609  
8610 /*
8611  * - LGPL
8612  *
8613  * Input
8614  * 
8615  */
8616
8617 /**
8618  * @class Roo.bootstrap.TextArea
8619  * @extends Roo.bootstrap.Input
8620  * Bootstrap TextArea class
8621  * @cfg {Number} cols Specifies the visible width of a text area
8622  * @cfg {Number} rows Specifies the visible number of lines in a text area
8623  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8624  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8625  * @cfg {string} html text
8626  * 
8627  * @constructor
8628  * Create a new TextArea
8629  * @param {Object} config The config object
8630  */
8631
8632 Roo.bootstrap.TextArea = function(config){
8633     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8634    
8635 };
8636
8637 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8638      
8639     cols : false,
8640     rows : 5,
8641     readOnly : false,
8642     warp : 'soft',
8643     resize : false,
8644     value: false,
8645     html: false,
8646     
8647     getAutoCreate : function(){
8648         
8649         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8650         
8651         var id = Roo.id();
8652         
8653         var cfg = {};
8654         
8655         var input =  {
8656             tag: 'textarea',
8657             id : id,
8658             warp : this.warp,
8659             rows : this.rows,
8660             value : this.value || '',
8661             html: this.html || '',
8662             cls : 'form-control',
8663             placeholder : this.placeholder || '' 
8664             
8665         };
8666         
8667         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8668             input.maxLength = this.maxLength;
8669         }
8670         
8671         if(this.resize){
8672             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8673         }
8674         
8675         if(this.cols){
8676             input.cols = this.cols;
8677         }
8678         
8679         if (this.readOnly) {
8680             input.readonly = true;
8681         }
8682         
8683         if (this.name) {
8684             input.name = this.name;
8685         }
8686         
8687         if (this.size) {
8688             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8689         }
8690         
8691         var settings=this;
8692         ['xs','sm','md','lg'].map(function(size){
8693             if (settings[size]) {
8694                 cfg.cls += ' col-' + size + '-' + settings[size];
8695             }
8696         });
8697         
8698         var inputblock = input;
8699         
8700         if(this.hasFeedback && !this.allowBlank){
8701             
8702             var feedback = {
8703                 tag: 'span',
8704                 cls: 'glyphicon form-control-feedback'
8705             };
8706
8707             inputblock = {
8708                 cls : 'has-feedback',
8709                 cn :  [
8710                     input,
8711                     feedback
8712                 ] 
8713             };  
8714         }
8715         
8716         
8717         if (this.before || this.after) {
8718             
8719             inputblock = {
8720                 cls : 'input-group',
8721                 cn :  [] 
8722             };
8723             if (this.before) {
8724                 inputblock.cn.push({
8725                     tag :'span',
8726                     cls : 'input-group-addon',
8727                     html : this.before
8728                 });
8729             }
8730             
8731             inputblock.cn.push(input);
8732             
8733             if(this.hasFeedback && !this.allowBlank){
8734                 inputblock.cls += ' has-feedback';
8735                 inputblock.cn.push(feedback);
8736             }
8737             
8738             if (this.after) {
8739                 inputblock.cn.push({
8740                     tag :'span',
8741                     cls : 'input-group-addon',
8742                     html : this.after
8743                 });
8744             }
8745             
8746         }
8747         
8748         if (align ==='left' && this.fieldLabel.length) {
8749 //                Roo.log("left and has label");
8750                 cfg.cn = [
8751                     
8752                     {
8753                         tag: 'label',
8754                         'for' :  id,
8755                         cls : 'control-label col-sm-' + this.labelWidth,
8756                         html : this.fieldLabel
8757                         
8758                     },
8759                     {
8760                         cls : "col-sm-" + (12 - this.labelWidth), 
8761                         cn: [
8762                             inputblock
8763                         ]
8764                     }
8765                     
8766                 ];
8767         } else if ( this.fieldLabel.length) {
8768 //                Roo.log(" label");
8769                  cfg.cn = [
8770                    
8771                     {
8772                         tag: 'label',
8773                         //cls : 'input-group-addon',
8774                         html : this.fieldLabel
8775                         
8776                     },
8777                     
8778                     inputblock
8779                     
8780                 ];
8781
8782         } else {
8783             
8784 //                   Roo.log(" no label && no align");
8785                 cfg.cn = [
8786                     
8787                         inputblock
8788                     
8789                 ];
8790                 
8791                 
8792         }
8793         
8794         if (this.disabled) {
8795             input.disabled=true;
8796         }
8797         
8798         return cfg;
8799         
8800     },
8801     /**
8802      * return the real textarea element.
8803      */
8804     inputEl: function ()
8805     {
8806         return this.el.select('textarea.form-control',true).first();
8807     },
8808     
8809     /**
8810      * Clear any invalid styles/messages for this field
8811      */
8812     clearInvalid : function()
8813     {
8814         
8815         if(!this.el || this.preventMark){ // not rendered
8816             return;
8817         }
8818         
8819         var label = this.el.select('label', true).first();
8820         var icon = this.el.select('i.fa-star', true).first();
8821         
8822         if(label && icon){
8823             icon.remove();
8824         }
8825         
8826         this.el.removeClass(this.invalidClass);
8827         
8828         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8829             
8830             var feedback = this.el.select('.form-control-feedback', true).first();
8831             
8832             if(feedback){
8833                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8834             }
8835             
8836         }
8837         
8838         this.fireEvent('valid', this);
8839     },
8840     
8841      /**
8842      * Mark this field as valid
8843      */
8844     markValid : function()
8845     {
8846         if(!this.el  || this.preventMark){ // not rendered
8847             return;
8848         }
8849         
8850         this.el.removeClass([this.invalidClass, this.validClass]);
8851         
8852         var feedback = this.el.select('.form-control-feedback', true).first();
8853             
8854         if(feedback){
8855             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8856         }
8857
8858         if(this.disabled || this.allowBlank){
8859             return;
8860         }
8861         
8862         var label = this.el.select('label', true).first();
8863         var icon = this.el.select('i.fa-star', true).first();
8864         
8865         if(label && icon){
8866             icon.remove();
8867         }
8868         
8869         this.el.addClass(this.validClass);
8870         
8871         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8872             
8873             var feedback = this.el.select('.form-control-feedback', true).first();
8874             
8875             if(feedback){
8876                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8877                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8878             }
8879             
8880         }
8881         
8882         this.fireEvent('valid', this);
8883     },
8884     
8885      /**
8886      * Mark this field as invalid
8887      * @param {String} msg The validation message
8888      */
8889     markInvalid : function(msg)
8890     {
8891         if(!this.el  || this.preventMark){ // not rendered
8892             return;
8893         }
8894         
8895         this.el.removeClass([this.invalidClass, this.validClass]);
8896         
8897         var feedback = this.el.select('.form-control-feedback', true).first();
8898             
8899         if(feedback){
8900             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8901         }
8902
8903         if(this.disabled || this.allowBlank){
8904             return;
8905         }
8906         
8907         var label = this.el.select('label', true).first();
8908         var icon = this.el.select('i.fa-star', true).first();
8909         
8910         if(!this.getValue().length && label && !icon){
8911             this.el.createChild({
8912                 tag : 'i',
8913                 cls : 'text-danger fa fa-lg fa-star',
8914                 tooltip : 'This field is required',
8915                 style : 'margin-right:5px;'
8916             }, label, true);
8917         }
8918
8919         this.el.addClass(this.invalidClass);
8920         
8921         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8922             
8923             var feedback = this.el.select('.form-control-feedback', true).first();
8924             
8925             if(feedback){
8926                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8927                 
8928                 if(this.getValue().length || this.forceFeedback){
8929                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8930                 }
8931                 
8932             }
8933             
8934         }
8935         
8936         this.fireEvent('invalid', this, msg);
8937     }
8938 });
8939
8940  
8941 /*
8942  * - LGPL
8943  *
8944  * trigger field - base class for combo..
8945  * 
8946  */
8947  
8948 /**
8949  * @class Roo.bootstrap.TriggerField
8950  * @extends Roo.bootstrap.Input
8951  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
8952  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
8953  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
8954  * for which you can provide a custom implementation.  For example:
8955  * <pre><code>
8956 var trigger = new Roo.bootstrap.TriggerField();
8957 trigger.onTriggerClick = myTriggerFn;
8958 trigger.applyTo('my-field');
8959 </code></pre>
8960  *
8961  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
8962  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
8963  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
8964  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
8965  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
8966
8967  * @constructor
8968  * Create a new TriggerField.
8969  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
8970  * to the base TextField)
8971  */
8972 Roo.bootstrap.TriggerField = function(config){
8973     this.mimicing = false;
8974     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
8975 };
8976
8977 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
8978     /**
8979      * @cfg {String} triggerClass A CSS class to apply to the trigger
8980      */
8981      /**
8982      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
8983      */
8984     hideTrigger:false,
8985
8986     /**
8987      * @cfg {Boolean} removable (true|false) special filter default false
8988      */
8989     removable : false,
8990     
8991     /** @cfg {Boolean} grow @hide */
8992     /** @cfg {Number} growMin @hide */
8993     /** @cfg {Number} growMax @hide */
8994
8995     /**
8996      * @hide 
8997      * @method
8998      */
8999     autoSize: Roo.emptyFn,
9000     // private
9001     monitorTab : true,
9002     // private
9003     deferHeight : true,
9004
9005     
9006     actionMode : 'wrap',
9007     
9008     caret : false,
9009     
9010     
9011     getAutoCreate : function(){
9012        
9013         var align = this.labelAlign || this.parentLabelAlign();
9014         
9015         var id = Roo.id();
9016         
9017         var cfg = {
9018             cls: 'form-group' //input-group
9019         };
9020         
9021         
9022         var input =  {
9023             tag: 'input',
9024             id : id,
9025             type : this.inputType,
9026             cls : 'form-control',
9027             autocomplete: 'new-password',
9028             placeholder : this.placeholder || '' 
9029             
9030         };
9031         if (this.name) {
9032             input.name = this.name;
9033         }
9034         if (this.size) {
9035             input.cls += ' input-' + this.size;
9036         }
9037         
9038         if (this.disabled) {
9039             input.disabled=true;
9040         }
9041         
9042         var inputblock = input;
9043         
9044         if(this.hasFeedback && !this.allowBlank){
9045             
9046             var feedback = {
9047                 tag: 'span',
9048                 cls: 'glyphicon form-control-feedback'
9049             };
9050             
9051             if(this.removable && !this.editable && !this.tickable){
9052                 inputblock = {
9053                     cls : 'has-feedback',
9054                     cn :  [
9055                         inputblock,
9056                         {
9057                             tag: 'button',
9058                             html : 'x',
9059                             cls : 'roo-combo-removable-btn close'
9060                         },
9061                         feedback
9062                     ] 
9063                 };
9064             } else {
9065                 inputblock = {
9066                     cls : 'has-feedback',
9067                     cn :  [
9068                         inputblock,
9069                         feedback
9070                     ] 
9071                 };
9072             }
9073
9074         } else {
9075             if(this.removable && !this.editable && !this.tickable){
9076                 inputblock = {
9077                     cls : 'roo-removable',
9078                     cn :  [
9079                         inputblock,
9080                         {
9081                             tag: 'button',
9082                             html : 'x',
9083                             cls : 'roo-combo-removable-btn close'
9084                         }
9085                     ] 
9086                 };
9087             }
9088         }
9089         
9090         if (this.before || this.after) {
9091             
9092             inputblock = {
9093                 cls : 'input-group',
9094                 cn :  [] 
9095             };
9096             if (this.before) {
9097                 inputblock.cn.push({
9098                     tag :'span',
9099                     cls : 'input-group-addon',
9100                     html : this.before
9101                 });
9102             }
9103             
9104             inputblock.cn.push(input);
9105             
9106             if(this.hasFeedback && !this.allowBlank){
9107                 inputblock.cls += ' has-feedback';
9108                 inputblock.cn.push(feedback);
9109             }
9110             
9111             if (this.after) {
9112                 inputblock.cn.push({
9113                     tag :'span',
9114                     cls : 'input-group-addon',
9115                     html : this.after
9116                 });
9117             }
9118             
9119         };
9120         
9121         var box = {
9122             tag: 'div',
9123             cn: [
9124                 {
9125                     tag: 'input',
9126                     type : 'hidden',
9127                     cls: 'form-hidden-field'
9128                 },
9129                 inputblock
9130             ]
9131             
9132         };
9133         
9134         if(this.multiple){
9135             box = {
9136                 tag: 'div',
9137                 cn: [
9138                     {
9139                         tag: 'input',
9140                         type : 'hidden',
9141                         cls: 'form-hidden-field'
9142                     },
9143                     {
9144                         tag: 'ul',
9145                         cls: 'select2-choices',
9146                         cn:[
9147                             {
9148                                 tag: 'li',
9149                                 cls: 'select2-search-field',
9150                                 cn: [
9151
9152                                     inputblock
9153                                 ]
9154                             }
9155                         ]
9156                     }
9157                 ]
9158             }
9159         };
9160         
9161         var combobox = {
9162             cls: 'select2-container input-group',
9163             cn: [
9164                 box
9165 //                {
9166 //                    tag: 'ul',
9167 //                    cls: 'typeahead typeahead-long dropdown-menu',
9168 //                    style: 'display:none'
9169 //                }
9170             ]
9171         };
9172         
9173         if(!this.multiple && this.showToggleBtn){
9174             
9175             var caret = {
9176                         tag: 'span',
9177                         cls: 'caret'
9178              };
9179             if (this.caret != false) {
9180                 caret = {
9181                      tag: 'i',
9182                      cls: 'fa fa-' + this.caret
9183                 };
9184                 
9185             }
9186             
9187             combobox.cn.push({
9188                 tag :'span',
9189                 cls : 'input-group-addon btn dropdown-toggle',
9190                 cn : [
9191                     caret,
9192                     {
9193                         tag: 'span',
9194                         cls: 'combobox-clear',
9195                         cn  : [
9196                             {
9197                                 tag : 'i',
9198                                 cls: 'icon-remove'
9199                             }
9200                         ]
9201                     }
9202                 ]
9203
9204             })
9205         }
9206         
9207         if(this.multiple){
9208             combobox.cls += ' select2-container-multi';
9209         }
9210         
9211         if (align ==='left' && this.fieldLabel.length) {
9212             
9213 //                Roo.log("left and has label");
9214                 cfg.cn = [
9215                     
9216                     {
9217                         tag: 'label',
9218                         'for' :  id,
9219                         cls : 'control-label col-sm-' + this.labelWidth,
9220                         html : this.fieldLabel
9221                         
9222                     },
9223                     {
9224                         cls : "col-sm-" + (12 - this.labelWidth), 
9225                         cn: [
9226                             combobox
9227                         ]
9228                     }
9229                     
9230                 ];
9231         } else if ( this.fieldLabel.length) {
9232 //                Roo.log(" label");
9233                  cfg.cn = [
9234                    
9235                     {
9236                         tag: 'label',
9237                         //cls : 'input-group-addon',
9238                         html : this.fieldLabel
9239                         
9240                     },
9241                     
9242                     combobox
9243                     
9244                 ];
9245
9246         } else {
9247             
9248 //                Roo.log(" no label && no align");
9249                 cfg = combobox
9250                      
9251                 
9252         }
9253          
9254         var settings=this;
9255         ['xs','sm','md','lg'].map(function(size){
9256             if (settings[size]) {
9257                 cfg.cls += ' col-' + size + '-' + settings[size];
9258             }
9259         });
9260         
9261         return cfg;
9262         
9263     },
9264     
9265     
9266     
9267     // private
9268     onResize : function(w, h){
9269 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9270 //        if(typeof w == 'number'){
9271 //            var x = w - this.trigger.getWidth();
9272 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9273 //            this.trigger.setStyle('left', x+'px');
9274 //        }
9275     },
9276
9277     // private
9278     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9279
9280     // private
9281     getResizeEl : function(){
9282         return this.inputEl();
9283     },
9284
9285     // private
9286     getPositionEl : function(){
9287         return this.inputEl();
9288     },
9289
9290     // private
9291     alignErrorIcon : function(){
9292         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9293     },
9294
9295     // private
9296     initEvents : function(){
9297         
9298         this.createList();
9299         
9300         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9301         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9302         if(!this.multiple && this.showToggleBtn){
9303             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9304             if(this.hideTrigger){
9305                 this.trigger.setDisplayed(false);
9306             }
9307             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9308         }
9309         
9310         if(this.multiple){
9311             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9312         }
9313         
9314         if(this.removable && !this.editable && !this.tickable){
9315             var close = this.closeTriggerEl();
9316             
9317             if(close){
9318                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9319                 close.on('click', this.removeBtnClick, this, close);
9320             }
9321         }
9322         
9323         //this.trigger.addClassOnOver('x-form-trigger-over');
9324         //this.trigger.addClassOnClick('x-form-trigger-click');
9325         
9326         //if(!this.width){
9327         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9328         //}
9329     },
9330     
9331     closeTriggerEl : function()
9332     {
9333         var close = this.el.select('.roo-combo-removable-btn', true).first();
9334         return close ? close : false;
9335     },
9336     
9337     removeBtnClick : function(e, h, el)
9338     {
9339         e.preventDefault();
9340         
9341         if(this.fireEvent("remove", this) !== false){
9342             this.reset();
9343         }
9344     },
9345     
9346     createList : function()
9347     {
9348         this.list = Roo.get(document.body).createChild({
9349             tag: 'ul',
9350             cls: 'typeahead typeahead-long dropdown-menu',
9351             style: 'display:none'
9352         });
9353         
9354         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9355         
9356     },
9357
9358     // private
9359     initTrigger : function(){
9360        
9361     },
9362
9363     // private
9364     onDestroy : function(){
9365         if(this.trigger){
9366             this.trigger.removeAllListeners();
9367           //  this.trigger.remove();
9368         }
9369         //if(this.wrap){
9370         //    this.wrap.remove();
9371         //}
9372         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9373     },
9374
9375     // private
9376     onFocus : function(){
9377         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9378         /*
9379         if(!this.mimicing){
9380             this.wrap.addClass('x-trigger-wrap-focus');
9381             this.mimicing = true;
9382             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9383             if(this.monitorTab){
9384                 this.el.on("keydown", this.checkTab, this);
9385             }
9386         }
9387         */
9388     },
9389
9390     // private
9391     checkTab : function(e){
9392         if(e.getKey() == e.TAB){
9393             this.triggerBlur();
9394         }
9395     },
9396
9397     // private
9398     onBlur : function(){
9399         // do nothing
9400     },
9401
9402     // private
9403     mimicBlur : function(e, t){
9404         /*
9405         if(!this.wrap.contains(t) && this.validateBlur()){
9406             this.triggerBlur();
9407         }
9408         */
9409     },
9410
9411     // private
9412     triggerBlur : function(){
9413         this.mimicing = false;
9414         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9415         if(this.monitorTab){
9416             this.el.un("keydown", this.checkTab, this);
9417         }
9418         //this.wrap.removeClass('x-trigger-wrap-focus');
9419         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9420     },
9421
9422     // private
9423     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9424     validateBlur : function(e, t){
9425         return true;
9426     },
9427
9428     // private
9429     onDisable : function(){
9430         this.inputEl().dom.disabled = true;
9431         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9432         //if(this.wrap){
9433         //    this.wrap.addClass('x-item-disabled');
9434         //}
9435     },
9436
9437     // private
9438     onEnable : function(){
9439         this.inputEl().dom.disabled = false;
9440         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9441         //if(this.wrap){
9442         //    this.el.removeClass('x-item-disabled');
9443         //}
9444     },
9445
9446     // private
9447     onShow : function(){
9448         var ae = this.getActionEl();
9449         
9450         if(ae){
9451             ae.dom.style.display = '';
9452             ae.dom.style.visibility = 'visible';
9453         }
9454     },
9455
9456     // private
9457     
9458     onHide : function(){
9459         var ae = this.getActionEl();
9460         ae.dom.style.display = 'none';
9461     },
9462
9463     /**
9464      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9465      * by an implementing function.
9466      * @method
9467      * @param {EventObject} e
9468      */
9469     onTriggerClick : Roo.emptyFn
9470 });
9471  /*
9472  * Based on:
9473  * Ext JS Library 1.1.1
9474  * Copyright(c) 2006-2007, Ext JS, LLC.
9475  *
9476  * Originally Released Under LGPL - original licence link has changed is not relivant.
9477  *
9478  * Fork - LGPL
9479  * <script type="text/javascript">
9480  */
9481
9482
9483 /**
9484  * @class Roo.data.SortTypes
9485  * @singleton
9486  * Defines the default sorting (casting?) comparison functions used when sorting data.
9487  */
9488 Roo.data.SortTypes = {
9489     /**
9490      * Default sort that does nothing
9491      * @param {Mixed} s The value being converted
9492      * @return {Mixed} The comparison value
9493      */
9494     none : function(s){
9495         return s;
9496     },
9497     
9498     /**
9499      * The regular expression used to strip tags
9500      * @type {RegExp}
9501      * @property
9502      */
9503     stripTagsRE : /<\/?[^>]+>/gi,
9504     
9505     /**
9506      * Strips all HTML tags to sort on text only
9507      * @param {Mixed} s The value being converted
9508      * @return {String} The comparison value
9509      */
9510     asText : function(s){
9511         return String(s).replace(this.stripTagsRE, "");
9512     },
9513     
9514     /**
9515      * Strips all HTML tags to sort on text only - Case insensitive
9516      * @param {Mixed} s The value being converted
9517      * @return {String} The comparison value
9518      */
9519     asUCText : function(s){
9520         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9521     },
9522     
9523     /**
9524      * Case insensitive string
9525      * @param {Mixed} s The value being converted
9526      * @return {String} The comparison value
9527      */
9528     asUCString : function(s) {
9529         return String(s).toUpperCase();
9530     },
9531     
9532     /**
9533      * Date sorting
9534      * @param {Mixed} s The value being converted
9535      * @return {Number} The comparison value
9536      */
9537     asDate : function(s) {
9538         if(!s){
9539             return 0;
9540         }
9541         if(s instanceof Date){
9542             return s.getTime();
9543         }
9544         return Date.parse(String(s));
9545     },
9546     
9547     /**
9548      * Float sorting
9549      * @param {Mixed} s The value being converted
9550      * @return {Float} The comparison value
9551      */
9552     asFloat : function(s) {
9553         var val = parseFloat(String(s).replace(/,/g, ""));
9554         if(isNaN(val)) {
9555             val = 0;
9556         }
9557         return val;
9558     },
9559     
9560     /**
9561      * Integer sorting
9562      * @param {Mixed} s The value being converted
9563      * @return {Number} The comparison value
9564      */
9565     asInt : function(s) {
9566         var val = parseInt(String(s).replace(/,/g, ""));
9567         if(isNaN(val)) {
9568             val = 0;
9569         }
9570         return val;
9571     }
9572 };/*
9573  * Based on:
9574  * Ext JS Library 1.1.1
9575  * Copyright(c) 2006-2007, Ext JS, LLC.
9576  *
9577  * Originally Released Under LGPL - original licence link has changed is not relivant.
9578  *
9579  * Fork - LGPL
9580  * <script type="text/javascript">
9581  */
9582
9583 /**
9584 * @class Roo.data.Record
9585  * Instances of this class encapsulate both record <em>definition</em> information, and record
9586  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9587  * to access Records cached in an {@link Roo.data.Store} object.<br>
9588  * <p>
9589  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9590  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9591  * objects.<br>
9592  * <p>
9593  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9594  * @constructor
9595  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9596  * {@link #create}. The parameters are the same.
9597  * @param {Array} data An associative Array of data values keyed by the field name.
9598  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9599  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9600  * not specified an integer id is generated.
9601  */
9602 Roo.data.Record = function(data, id){
9603     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9604     this.data = data;
9605 };
9606
9607 /**
9608  * Generate a constructor for a specific record layout.
9609  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9610  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9611  * Each field definition object may contain the following properties: <ul>
9612  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
9613  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9614  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9615  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9616  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9617  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9618  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9619  * this may be omitted.</p></li>
9620  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9621  * <ul><li>auto (Default, implies no conversion)</li>
9622  * <li>string</li>
9623  * <li>int</li>
9624  * <li>float</li>
9625  * <li>boolean</li>
9626  * <li>date</li></ul></p></li>
9627  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9628  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9629  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9630  * by the Reader into an object that will be stored in the Record. It is passed the
9631  * following parameters:<ul>
9632  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9633  * </ul></p></li>
9634  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9635  * </ul>
9636  * <br>usage:<br><pre><code>
9637 var TopicRecord = Roo.data.Record.create(
9638     {name: 'title', mapping: 'topic_title'},
9639     {name: 'author', mapping: 'username'},
9640     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9641     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9642     {name: 'lastPoster', mapping: 'user2'},
9643     {name: 'excerpt', mapping: 'post_text'}
9644 );
9645
9646 var myNewRecord = new TopicRecord({
9647     title: 'Do my job please',
9648     author: 'noobie',
9649     totalPosts: 1,
9650     lastPost: new Date(),
9651     lastPoster: 'Animal',
9652     excerpt: 'No way dude!'
9653 });
9654 myStore.add(myNewRecord);
9655 </code></pre>
9656  * @method create
9657  * @static
9658  */
9659 Roo.data.Record.create = function(o){
9660     var f = function(){
9661         f.superclass.constructor.apply(this, arguments);
9662     };
9663     Roo.extend(f, Roo.data.Record);
9664     var p = f.prototype;
9665     p.fields = new Roo.util.MixedCollection(false, function(field){
9666         return field.name;
9667     });
9668     for(var i = 0, len = o.length; i < len; i++){
9669         p.fields.add(new Roo.data.Field(o[i]));
9670     }
9671     f.getField = function(name){
9672         return p.fields.get(name);  
9673     };
9674     return f;
9675 };
9676
9677 Roo.data.Record.AUTO_ID = 1000;
9678 Roo.data.Record.EDIT = 'edit';
9679 Roo.data.Record.REJECT = 'reject';
9680 Roo.data.Record.COMMIT = 'commit';
9681
9682 Roo.data.Record.prototype = {
9683     /**
9684      * Readonly flag - true if this record has been modified.
9685      * @type Boolean
9686      */
9687     dirty : false,
9688     editing : false,
9689     error: null,
9690     modified: null,
9691
9692     // private
9693     join : function(store){
9694         this.store = store;
9695     },
9696
9697     /**
9698      * Set the named field to the specified value.
9699      * @param {String} name The name of the field to set.
9700      * @param {Object} value The value to set the field to.
9701      */
9702     set : function(name, value){
9703         if(this.data[name] == value){
9704             return;
9705         }
9706         this.dirty = true;
9707         if(!this.modified){
9708             this.modified = {};
9709         }
9710         if(typeof this.modified[name] == 'undefined'){
9711             this.modified[name] = this.data[name];
9712         }
9713         this.data[name] = value;
9714         if(!this.editing && this.store){
9715             this.store.afterEdit(this);
9716         }       
9717     },
9718
9719     /**
9720      * Get the value of the named field.
9721      * @param {String} name The name of the field to get the value of.
9722      * @return {Object} The value of the field.
9723      */
9724     get : function(name){
9725         return this.data[name]; 
9726     },
9727
9728     // private
9729     beginEdit : function(){
9730         this.editing = true;
9731         this.modified = {}; 
9732     },
9733
9734     // private
9735     cancelEdit : function(){
9736         this.editing = false;
9737         delete this.modified;
9738     },
9739
9740     // private
9741     endEdit : function(){
9742         this.editing = false;
9743         if(this.dirty && this.store){
9744             this.store.afterEdit(this);
9745         }
9746     },
9747
9748     /**
9749      * Usually called by the {@link Roo.data.Store} which owns the Record.
9750      * Rejects all changes made to the Record since either creation, or the last commit operation.
9751      * Modified fields are reverted to their original values.
9752      * <p>
9753      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9754      * of reject operations.
9755      */
9756     reject : function(){
9757         var m = this.modified;
9758         for(var n in m){
9759             if(typeof m[n] != "function"){
9760                 this.data[n] = m[n];
9761             }
9762         }
9763         this.dirty = false;
9764         delete this.modified;
9765         this.editing = false;
9766         if(this.store){
9767             this.store.afterReject(this);
9768         }
9769     },
9770
9771     /**
9772      * Usually called by the {@link Roo.data.Store} which owns the Record.
9773      * Commits all changes made to the Record since either creation, or the last commit operation.
9774      * <p>
9775      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
9776      * of commit operations.
9777      */
9778     commit : function(){
9779         this.dirty = false;
9780         delete this.modified;
9781         this.editing = false;
9782         if(this.store){
9783             this.store.afterCommit(this);
9784         }
9785     },
9786
9787     // private
9788     hasError : function(){
9789         return this.error != null;
9790     },
9791
9792     // private
9793     clearError : function(){
9794         this.error = null;
9795     },
9796
9797     /**
9798      * Creates a copy of this record.
9799      * @param {String} id (optional) A new record id if you don't want to use this record's id
9800      * @return {Record}
9801      */
9802     copy : function(newId) {
9803         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
9804     }
9805 };/*
9806  * Based on:
9807  * Ext JS Library 1.1.1
9808  * Copyright(c) 2006-2007, Ext JS, LLC.
9809  *
9810  * Originally Released Under LGPL - original licence link has changed is not relivant.
9811  *
9812  * Fork - LGPL
9813  * <script type="text/javascript">
9814  */
9815
9816
9817
9818 /**
9819  * @class Roo.data.Store
9820  * @extends Roo.util.Observable
9821  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
9822  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
9823  * <p>
9824  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
9825  * has no knowledge of the format of the data returned by the Proxy.<br>
9826  * <p>
9827  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
9828  * instances from the data object. These records are cached and made available through accessor functions.
9829  * @constructor
9830  * Creates a new Store.
9831  * @param {Object} config A config object containing the objects needed for the Store to access data,
9832  * and read the data into Records.
9833  */
9834 Roo.data.Store = function(config){
9835     this.data = new Roo.util.MixedCollection(false);
9836     this.data.getKey = function(o){
9837         return o.id;
9838     };
9839     this.baseParams = {};
9840     // private
9841     this.paramNames = {
9842         "start" : "start",
9843         "limit" : "limit",
9844         "sort" : "sort",
9845         "dir" : "dir",
9846         "multisort" : "_multisort"
9847     };
9848
9849     if(config && config.data){
9850         this.inlineData = config.data;
9851         delete config.data;
9852     }
9853
9854     Roo.apply(this, config);
9855     
9856     if(this.reader){ // reader passed
9857         this.reader = Roo.factory(this.reader, Roo.data);
9858         this.reader.xmodule = this.xmodule || false;
9859         if(!this.recordType){
9860             this.recordType = this.reader.recordType;
9861         }
9862         if(this.reader.onMetaChange){
9863             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
9864         }
9865     }
9866
9867     if(this.recordType){
9868         this.fields = this.recordType.prototype.fields;
9869     }
9870     this.modified = [];
9871
9872     this.addEvents({
9873         /**
9874          * @event datachanged
9875          * Fires when the data cache has changed, and a widget which is using this Store
9876          * as a Record cache should refresh its view.
9877          * @param {Store} this
9878          */
9879         datachanged : true,
9880         /**
9881          * @event metachange
9882          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
9883          * @param {Store} this
9884          * @param {Object} meta The JSON metadata
9885          */
9886         metachange : true,
9887         /**
9888          * @event add
9889          * Fires when Records have been added to the Store
9890          * @param {Store} this
9891          * @param {Roo.data.Record[]} records The array of Records added
9892          * @param {Number} index The index at which the record(s) were added
9893          */
9894         add : true,
9895         /**
9896          * @event remove
9897          * Fires when a Record has been removed from the Store
9898          * @param {Store} this
9899          * @param {Roo.data.Record} record The Record that was removed
9900          * @param {Number} index The index at which the record was removed
9901          */
9902         remove : true,
9903         /**
9904          * @event update
9905          * Fires when a Record has been updated
9906          * @param {Store} this
9907          * @param {Roo.data.Record} record The Record that was updated
9908          * @param {String} operation The update operation being performed.  Value may be one of:
9909          * <pre><code>
9910  Roo.data.Record.EDIT
9911  Roo.data.Record.REJECT
9912  Roo.data.Record.COMMIT
9913          * </code></pre>
9914          */
9915         update : true,
9916         /**
9917          * @event clear
9918          * Fires when the data cache has been cleared.
9919          * @param {Store} this
9920          */
9921         clear : true,
9922         /**
9923          * @event beforeload
9924          * Fires before a request is made for a new data object.  If the beforeload handler returns false
9925          * the load action will be canceled.
9926          * @param {Store} this
9927          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9928          */
9929         beforeload : true,
9930         /**
9931          * @event beforeloadadd
9932          * Fires after a new set of Records has been loaded.
9933          * @param {Store} this
9934          * @param {Roo.data.Record[]} records The Records that were loaded
9935          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9936          */
9937         beforeloadadd : true,
9938         /**
9939          * @event load
9940          * Fires after a new set of Records has been loaded, before they are added to the store.
9941          * @param {Store} this
9942          * @param {Roo.data.Record[]} records The Records that were loaded
9943          * @param {Object} options The loading options that were specified (see {@link #load} for details)
9944          * @params {Object} return from reader
9945          */
9946         load : true,
9947         /**
9948          * @event loadexception
9949          * Fires if an exception occurs in the Proxy during loading.
9950          * Called with the signature of the Proxy's "loadexception" event.
9951          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
9952          * 
9953          * @param {Proxy} 
9954          * @param {Object} return from JsonData.reader() - success, totalRecords, records
9955          * @param {Object} load options 
9956          * @param {Object} jsonData from your request (normally this contains the Exception)
9957          */
9958         loadexception : true
9959     });
9960     
9961     if(this.proxy){
9962         this.proxy = Roo.factory(this.proxy, Roo.data);
9963         this.proxy.xmodule = this.xmodule || false;
9964         this.relayEvents(this.proxy,  ["loadexception"]);
9965     }
9966     this.sortToggle = {};
9967     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
9968
9969     Roo.data.Store.superclass.constructor.call(this);
9970
9971     if(this.inlineData){
9972         this.loadData(this.inlineData);
9973         delete this.inlineData;
9974     }
9975 };
9976
9977 Roo.extend(Roo.data.Store, Roo.util.Observable, {
9978      /**
9979     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
9980     * without a remote query - used by combo/forms at present.
9981     */
9982     
9983     /**
9984     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
9985     */
9986     /**
9987     * @cfg {Array} data Inline data to be loaded when the store is initialized.
9988     */
9989     /**
9990     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
9991     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
9992     */
9993     /**
9994     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
9995     * on any HTTP request
9996     */
9997     /**
9998     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
9999     */
10000     /**
10001     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10002     */
10003     multiSort: false,
10004     /**
10005     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10006     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10007     */
10008     remoteSort : false,
10009
10010     /**
10011     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10012      * loaded or when a record is removed. (defaults to false).
10013     */
10014     pruneModifiedRecords : false,
10015
10016     // private
10017     lastOptions : null,
10018
10019     /**
10020      * Add Records to the Store and fires the add event.
10021      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10022      */
10023     add : function(records){
10024         records = [].concat(records);
10025         for(var i = 0, len = records.length; i < len; i++){
10026             records[i].join(this);
10027         }
10028         var index = this.data.length;
10029         this.data.addAll(records);
10030         this.fireEvent("add", this, records, index);
10031     },
10032
10033     /**
10034      * Remove a Record from the Store and fires the remove event.
10035      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10036      */
10037     remove : function(record){
10038         var index = this.data.indexOf(record);
10039         this.data.removeAt(index);
10040         if(this.pruneModifiedRecords){
10041             this.modified.remove(record);
10042         }
10043         this.fireEvent("remove", this, record, index);
10044     },
10045
10046     /**
10047      * Remove all Records from the Store and fires the clear event.
10048      */
10049     removeAll : function(){
10050         this.data.clear();
10051         if(this.pruneModifiedRecords){
10052             this.modified = [];
10053         }
10054         this.fireEvent("clear", this);
10055     },
10056
10057     /**
10058      * Inserts Records to the Store at the given index and fires the add event.
10059      * @param {Number} index The start index at which to insert the passed Records.
10060      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10061      */
10062     insert : function(index, records){
10063         records = [].concat(records);
10064         for(var i = 0, len = records.length; i < len; i++){
10065             this.data.insert(index, records[i]);
10066             records[i].join(this);
10067         }
10068         this.fireEvent("add", this, records, index);
10069     },
10070
10071     /**
10072      * Get the index within the cache of the passed Record.
10073      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10074      * @return {Number} The index of the passed Record. Returns -1 if not found.
10075      */
10076     indexOf : function(record){
10077         return this.data.indexOf(record);
10078     },
10079
10080     /**
10081      * Get the index within the cache of the Record with the passed id.
10082      * @param {String} id The id of the Record to find.
10083      * @return {Number} The index of the Record. Returns -1 if not found.
10084      */
10085     indexOfId : function(id){
10086         return this.data.indexOfKey(id);
10087     },
10088
10089     /**
10090      * Get the Record with the specified id.
10091      * @param {String} id The id of the Record to find.
10092      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10093      */
10094     getById : function(id){
10095         return this.data.key(id);
10096     },
10097
10098     /**
10099      * Get the Record at the specified index.
10100      * @param {Number} index The index of the Record to find.
10101      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10102      */
10103     getAt : function(index){
10104         return this.data.itemAt(index);
10105     },
10106
10107     /**
10108      * Returns a range of Records between specified indices.
10109      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10110      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10111      * @return {Roo.data.Record[]} An array of Records
10112      */
10113     getRange : function(start, end){
10114         return this.data.getRange(start, end);
10115     },
10116
10117     // private
10118     storeOptions : function(o){
10119         o = Roo.apply({}, o);
10120         delete o.callback;
10121         delete o.scope;
10122         this.lastOptions = o;
10123     },
10124
10125     /**
10126      * Loads the Record cache from the configured Proxy using the configured Reader.
10127      * <p>
10128      * If using remote paging, then the first load call must specify the <em>start</em>
10129      * and <em>limit</em> properties in the options.params property to establish the initial
10130      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10131      * <p>
10132      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10133      * and this call will return before the new data has been loaded. Perform any post-processing
10134      * in a callback function, or in a "load" event handler.</strong>
10135      * <p>
10136      * @param {Object} options An object containing properties which control loading options:<ul>
10137      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10138      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10139      * passed the following arguments:<ul>
10140      * <li>r : Roo.data.Record[]</li>
10141      * <li>options: Options object from the load call</li>
10142      * <li>success: Boolean success indicator</li></ul></li>
10143      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10144      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10145      * </ul>
10146      */
10147     load : function(options){
10148         options = options || {};
10149         if(this.fireEvent("beforeload", this, options) !== false){
10150             this.storeOptions(options);
10151             var p = Roo.apply(options.params || {}, this.baseParams);
10152             // if meta was not loaded from remote source.. try requesting it.
10153             if (!this.reader.metaFromRemote) {
10154                 p._requestMeta = 1;
10155             }
10156             if(this.sortInfo && this.remoteSort){
10157                 var pn = this.paramNames;
10158                 p[pn["sort"]] = this.sortInfo.field;
10159                 p[pn["dir"]] = this.sortInfo.direction;
10160             }
10161             if (this.multiSort) {
10162                 var pn = this.paramNames;
10163                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10164             }
10165             
10166             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10167         }
10168     },
10169
10170     /**
10171      * Reloads the Record cache from the configured Proxy using the configured Reader and
10172      * the options from the last load operation performed.
10173      * @param {Object} options (optional) An object containing properties which may override the options
10174      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10175      * the most recently used options are reused).
10176      */
10177     reload : function(options){
10178         this.load(Roo.applyIf(options||{}, this.lastOptions));
10179     },
10180
10181     // private
10182     // Called as a callback by the Reader during a load operation.
10183     loadRecords : function(o, options, success){
10184         if(!o || success === false){
10185             if(success !== false){
10186                 this.fireEvent("load", this, [], options, o);
10187             }
10188             if(options.callback){
10189                 options.callback.call(options.scope || this, [], options, false);
10190             }
10191             return;
10192         }
10193         // if data returned failure - throw an exception.
10194         if (o.success === false) {
10195             // show a message if no listener is registered.
10196             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10197                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10198             }
10199             // loadmask wil be hooked into this..
10200             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10201             return;
10202         }
10203         var r = o.records, t = o.totalRecords || r.length;
10204         
10205         this.fireEvent("beforeloadadd", this, r, options, o);
10206         
10207         if(!options || options.add !== true){
10208             if(this.pruneModifiedRecords){
10209                 this.modified = [];
10210             }
10211             for(var i = 0, len = r.length; i < len; i++){
10212                 r[i].join(this);
10213             }
10214             if(this.snapshot){
10215                 this.data = this.snapshot;
10216                 delete this.snapshot;
10217             }
10218             this.data.clear();
10219             this.data.addAll(r);
10220             this.totalLength = t;
10221             this.applySort();
10222             this.fireEvent("datachanged", this);
10223         }else{
10224             this.totalLength = Math.max(t, this.data.length+r.length);
10225             this.add(r);
10226         }
10227         this.fireEvent("load", this, r, options, o);
10228         if(options.callback){
10229             options.callback.call(options.scope || this, r, options, true);
10230         }
10231     },
10232
10233
10234     /**
10235      * Loads data from a passed data block. A Reader which understands the format of the data
10236      * must have been configured in the constructor.
10237      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10238      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10239      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10240      */
10241     loadData : function(o, append){
10242         var r = this.reader.readRecords(o);
10243         this.loadRecords(r, {add: append}, true);
10244     },
10245
10246     /**
10247      * Gets the number of cached records.
10248      * <p>
10249      * <em>If using paging, this may not be the total size of the dataset. If the data object
10250      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10251      * the data set size</em>
10252      */
10253     getCount : function(){
10254         return this.data.length || 0;
10255     },
10256
10257     /**
10258      * Gets the total number of records in the dataset as returned by the server.
10259      * <p>
10260      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10261      * the dataset size</em>
10262      */
10263     getTotalCount : function(){
10264         return this.totalLength || 0;
10265     },
10266
10267     /**
10268      * Returns the sort state of the Store as an object with two properties:
10269      * <pre><code>
10270  field {String} The name of the field by which the Records are sorted
10271  direction {String} The sort order, "ASC" or "DESC"
10272      * </code></pre>
10273      */
10274     getSortState : function(){
10275         return this.sortInfo;
10276     },
10277
10278     // private
10279     applySort : function(){
10280         if(this.sortInfo && !this.remoteSort){
10281             var s = this.sortInfo, f = s.field;
10282             var st = this.fields.get(f).sortType;
10283             var fn = function(r1, r2){
10284                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10285                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10286             };
10287             this.data.sort(s.direction, fn);
10288             if(this.snapshot && this.snapshot != this.data){
10289                 this.snapshot.sort(s.direction, fn);
10290             }
10291         }
10292     },
10293
10294     /**
10295      * Sets the default sort column and order to be used by the next load operation.
10296      * @param {String} fieldName The name of the field to sort by.
10297      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10298      */
10299     setDefaultSort : function(field, dir){
10300         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10301     },
10302
10303     /**
10304      * Sort the Records.
10305      * If remote sorting is used, the sort is performed on the server, and the cache is
10306      * reloaded. If local sorting is used, the cache is sorted internally.
10307      * @param {String} fieldName The name of the field to sort by.
10308      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10309      */
10310     sort : function(fieldName, dir){
10311         var f = this.fields.get(fieldName);
10312         if(!dir){
10313             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10314             
10315             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10316                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10317             }else{
10318                 dir = f.sortDir;
10319             }
10320         }
10321         this.sortToggle[f.name] = dir;
10322         this.sortInfo = {field: f.name, direction: dir};
10323         if(!this.remoteSort){
10324             this.applySort();
10325             this.fireEvent("datachanged", this);
10326         }else{
10327             this.load(this.lastOptions);
10328         }
10329     },
10330
10331     /**
10332      * Calls the specified function for each of the Records in the cache.
10333      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10334      * Returning <em>false</em> aborts and exits the iteration.
10335      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10336      */
10337     each : function(fn, scope){
10338         this.data.each(fn, scope);
10339     },
10340
10341     /**
10342      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10343      * (e.g., during paging).
10344      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10345      */
10346     getModifiedRecords : function(){
10347         return this.modified;
10348     },
10349
10350     // private
10351     createFilterFn : function(property, value, anyMatch){
10352         if(!value.exec){ // not a regex
10353             value = String(value);
10354             if(value.length == 0){
10355                 return false;
10356             }
10357             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10358         }
10359         return function(r){
10360             return value.test(r.data[property]);
10361         };
10362     },
10363
10364     /**
10365      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10366      * @param {String} property A field on your records
10367      * @param {Number} start The record index to start at (defaults to 0)
10368      * @param {Number} end The last record index to include (defaults to length - 1)
10369      * @return {Number} The sum
10370      */
10371     sum : function(property, start, end){
10372         var rs = this.data.items, v = 0;
10373         start = start || 0;
10374         end = (end || end === 0) ? end : rs.length-1;
10375
10376         for(var i = start; i <= end; i++){
10377             v += (rs[i].data[property] || 0);
10378         }
10379         return v;
10380     },
10381
10382     /**
10383      * Filter the records by a specified property.
10384      * @param {String} field A field on your records
10385      * @param {String/RegExp} value Either a string that the field
10386      * should start with or a RegExp to test against the field
10387      * @param {Boolean} anyMatch True to match any part not just the beginning
10388      */
10389     filter : function(property, value, anyMatch){
10390         var fn = this.createFilterFn(property, value, anyMatch);
10391         return fn ? this.filterBy(fn) : this.clearFilter();
10392     },
10393
10394     /**
10395      * Filter by a function. The specified function will be called with each
10396      * record in this data source. If the function returns true the record is included,
10397      * otherwise it is filtered.
10398      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10399      * @param {Object} scope (optional) The scope of the function (defaults to this)
10400      */
10401     filterBy : function(fn, scope){
10402         this.snapshot = this.snapshot || this.data;
10403         this.data = this.queryBy(fn, scope||this);
10404         this.fireEvent("datachanged", this);
10405     },
10406
10407     /**
10408      * Query the records by a specified property.
10409      * @param {String} field A field on your records
10410      * @param {String/RegExp} value Either a string that the field
10411      * should start with or a RegExp to test against the field
10412      * @param {Boolean} anyMatch True to match any part not just the beginning
10413      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10414      */
10415     query : function(property, value, anyMatch){
10416         var fn = this.createFilterFn(property, value, anyMatch);
10417         return fn ? this.queryBy(fn) : this.data.clone();
10418     },
10419
10420     /**
10421      * Query by a function. The specified function will be called with each
10422      * record in this data source. If the function returns true the record is included
10423      * in the results.
10424      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10425      * @param {Object} scope (optional) The scope of the function (defaults to this)
10426       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10427      **/
10428     queryBy : function(fn, scope){
10429         var data = this.snapshot || this.data;
10430         return data.filterBy(fn, scope||this);
10431     },
10432
10433     /**
10434      * Collects unique values for a particular dataIndex from this store.
10435      * @param {String} dataIndex The property to collect
10436      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10437      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10438      * @return {Array} An array of the unique values
10439      **/
10440     collect : function(dataIndex, allowNull, bypassFilter){
10441         var d = (bypassFilter === true && this.snapshot) ?
10442                 this.snapshot.items : this.data.items;
10443         var v, sv, r = [], l = {};
10444         for(var i = 0, len = d.length; i < len; i++){
10445             v = d[i].data[dataIndex];
10446             sv = String(v);
10447             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10448                 l[sv] = true;
10449                 r[r.length] = v;
10450             }
10451         }
10452         return r;
10453     },
10454
10455     /**
10456      * Revert to a view of the Record cache with no filtering applied.
10457      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10458      */
10459     clearFilter : function(suppressEvent){
10460         if(this.snapshot && this.snapshot != this.data){
10461             this.data = this.snapshot;
10462             delete this.snapshot;
10463             if(suppressEvent !== true){
10464                 this.fireEvent("datachanged", this);
10465             }
10466         }
10467     },
10468
10469     // private
10470     afterEdit : function(record){
10471         if(this.modified.indexOf(record) == -1){
10472             this.modified.push(record);
10473         }
10474         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10475     },
10476     
10477     // private
10478     afterReject : function(record){
10479         this.modified.remove(record);
10480         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10481     },
10482
10483     // private
10484     afterCommit : function(record){
10485         this.modified.remove(record);
10486         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10487     },
10488
10489     /**
10490      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10491      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10492      */
10493     commitChanges : function(){
10494         var m = this.modified.slice(0);
10495         this.modified = [];
10496         for(var i = 0, len = m.length; i < len; i++){
10497             m[i].commit();
10498         }
10499     },
10500
10501     /**
10502      * Cancel outstanding changes on all changed records.
10503      */
10504     rejectChanges : function(){
10505         var m = this.modified.slice(0);
10506         this.modified = [];
10507         for(var i = 0, len = m.length; i < len; i++){
10508             m[i].reject();
10509         }
10510     },
10511
10512     onMetaChange : function(meta, rtype, o){
10513         this.recordType = rtype;
10514         this.fields = rtype.prototype.fields;
10515         delete this.snapshot;
10516         this.sortInfo = meta.sortInfo || this.sortInfo;
10517         this.modified = [];
10518         this.fireEvent('metachange', this, this.reader.meta);
10519     },
10520     
10521     moveIndex : function(data, type)
10522     {
10523         var index = this.indexOf(data);
10524         
10525         var newIndex = index + type;
10526         
10527         this.remove(data);
10528         
10529         this.insert(newIndex, data);
10530         
10531     }
10532 });/*
10533  * Based on:
10534  * Ext JS Library 1.1.1
10535  * Copyright(c) 2006-2007, Ext JS, LLC.
10536  *
10537  * Originally Released Under LGPL - original licence link has changed is not relivant.
10538  *
10539  * Fork - LGPL
10540  * <script type="text/javascript">
10541  */
10542
10543 /**
10544  * @class Roo.data.SimpleStore
10545  * @extends Roo.data.Store
10546  * Small helper class to make creating Stores from Array data easier.
10547  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10548  * @cfg {Array} fields An array of field definition objects, or field name strings.
10549  * @cfg {Array} data The multi-dimensional array of data
10550  * @constructor
10551  * @param {Object} config
10552  */
10553 Roo.data.SimpleStore = function(config){
10554     Roo.data.SimpleStore.superclass.constructor.call(this, {
10555         isLocal : true,
10556         reader: new Roo.data.ArrayReader({
10557                 id: config.id
10558             },
10559             Roo.data.Record.create(config.fields)
10560         ),
10561         proxy : new Roo.data.MemoryProxy(config.data)
10562     });
10563     this.load();
10564 };
10565 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10566  * Based on:
10567  * Ext JS Library 1.1.1
10568  * Copyright(c) 2006-2007, Ext JS, LLC.
10569  *
10570  * Originally Released Under LGPL - original licence link has changed is not relivant.
10571  *
10572  * Fork - LGPL
10573  * <script type="text/javascript">
10574  */
10575
10576 /**
10577 /**
10578  * @extends Roo.data.Store
10579  * @class Roo.data.JsonStore
10580  * Small helper class to make creating Stores for JSON data easier. <br/>
10581 <pre><code>
10582 var store = new Roo.data.JsonStore({
10583     url: 'get-images.php',
10584     root: 'images',
10585     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10586 });
10587 </code></pre>
10588  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10589  * JsonReader and HttpProxy (unless inline data is provided).</b>
10590  * @cfg {Array} fields An array of field definition objects, or field name strings.
10591  * @constructor
10592  * @param {Object} config
10593  */
10594 Roo.data.JsonStore = function(c){
10595     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10596         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10597         reader: new Roo.data.JsonReader(c, c.fields)
10598     }));
10599 };
10600 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10601  * Based on:
10602  * Ext JS Library 1.1.1
10603  * Copyright(c) 2006-2007, Ext JS, LLC.
10604  *
10605  * Originally Released Under LGPL - original licence link has changed is not relivant.
10606  *
10607  * Fork - LGPL
10608  * <script type="text/javascript">
10609  */
10610
10611  
10612 Roo.data.Field = function(config){
10613     if(typeof config == "string"){
10614         config = {name: config};
10615     }
10616     Roo.apply(this, config);
10617     
10618     if(!this.type){
10619         this.type = "auto";
10620     }
10621     
10622     var st = Roo.data.SortTypes;
10623     // named sortTypes are supported, here we look them up
10624     if(typeof this.sortType == "string"){
10625         this.sortType = st[this.sortType];
10626     }
10627     
10628     // set default sortType for strings and dates
10629     if(!this.sortType){
10630         switch(this.type){
10631             case "string":
10632                 this.sortType = st.asUCString;
10633                 break;
10634             case "date":
10635                 this.sortType = st.asDate;
10636                 break;
10637             default:
10638                 this.sortType = st.none;
10639         }
10640     }
10641
10642     // define once
10643     var stripRe = /[\$,%]/g;
10644
10645     // prebuilt conversion function for this field, instead of
10646     // switching every time we're reading a value
10647     if(!this.convert){
10648         var cv, dateFormat = this.dateFormat;
10649         switch(this.type){
10650             case "":
10651             case "auto":
10652             case undefined:
10653                 cv = function(v){ return v; };
10654                 break;
10655             case "string":
10656                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10657                 break;
10658             case "int":
10659                 cv = function(v){
10660                     return v !== undefined && v !== null && v !== '' ?
10661                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10662                     };
10663                 break;
10664             case "float":
10665                 cv = function(v){
10666                     return v !== undefined && v !== null && v !== '' ?
10667                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10668                     };
10669                 break;
10670             case "bool":
10671             case "boolean":
10672                 cv = function(v){ return v === true || v === "true" || v == 1; };
10673                 break;
10674             case "date":
10675                 cv = function(v){
10676                     if(!v){
10677                         return '';
10678                     }
10679                     if(v instanceof Date){
10680                         return v;
10681                     }
10682                     if(dateFormat){
10683                         if(dateFormat == "timestamp"){
10684                             return new Date(v*1000);
10685                         }
10686                         return Date.parseDate(v, dateFormat);
10687                     }
10688                     var parsed = Date.parse(v);
10689                     return parsed ? new Date(parsed) : null;
10690                 };
10691              break;
10692             
10693         }
10694         this.convert = cv;
10695     }
10696 };
10697
10698 Roo.data.Field.prototype = {
10699     dateFormat: null,
10700     defaultValue: "",
10701     mapping: null,
10702     sortType : null,
10703     sortDir : "ASC"
10704 };/*
10705  * Based on:
10706  * Ext JS Library 1.1.1
10707  * Copyright(c) 2006-2007, Ext JS, LLC.
10708  *
10709  * Originally Released Under LGPL - original licence link has changed is not relivant.
10710  *
10711  * Fork - LGPL
10712  * <script type="text/javascript">
10713  */
10714  
10715 // Base class for reading structured data from a data source.  This class is intended to be
10716 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10717
10718 /**
10719  * @class Roo.data.DataReader
10720  * Base class for reading structured data from a data source.  This class is intended to be
10721  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10722  */
10723
10724 Roo.data.DataReader = function(meta, recordType){
10725     
10726     this.meta = meta;
10727     
10728     this.recordType = recordType instanceof Array ? 
10729         Roo.data.Record.create(recordType) : recordType;
10730 };
10731
10732 Roo.data.DataReader.prototype = {
10733      /**
10734      * Create an empty record
10735      * @param {Object} data (optional) - overlay some values
10736      * @return {Roo.data.Record} record created.
10737      */
10738     newRow :  function(d) {
10739         var da =  {};
10740         this.recordType.prototype.fields.each(function(c) {
10741             switch( c.type) {
10742                 case 'int' : da[c.name] = 0; break;
10743                 case 'date' : da[c.name] = new Date(); break;
10744                 case 'float' : da[c.name] = 0.0; break;
10745                 case 'boolean' : da[c.name] = false; break;
10746                 default : da[c.name] = ""; break;
10747             }
10748             
10749         });
10750         return new this.recordType(Roo.apply(da, d));
10751     }
10752     
10753 };/*
10754  * Based on:
10755  * Ext JS Library 1.1.1
10756  * Copyright(c) 2006-2007, Ext JS, LLC.
10757  *
10758  * Originally Released Under LGPL - original licence link has changed is not relivant.
10759  *
10760  * Fork - LGPL
10761  * <script type="text/javascript">
10762  */
10763
10764 /**
10765  * @class Roo.data.DataProxy
10766  * @extends Roo.data.Observable
10767  * This class is an abstract base class for implementations which provide retrieval of
10768  * unformatted data objects.<br>
10769  * <p>
10770  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
10771  * (of the appropriate type which knows how to parse the data object) to provide a block of
10772  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
10773  * <p>
10774  * Custom implementations must implement the load method as described in
10775  * {@link Roo.data.HttpProxy#load}.
10776  */
10777 Roo.data.DataProxy = function(){
10778     this.addEvents({
10779         /**
10780          * @event beforeload
10781          * Fires before a network request is made to retrieve a data object.
10782          * @param {Object} This DataProxy object.
10783          * @param {Object} params The params parameter to the load function.
10784          */
10785         beforeload : true,
10786         /**
10787          * @event load
10788          * Fires before the load method's callback is called.
10789          * @param {Object} This DataProxy object.
10790          * @param {Object} o The data object.
10791          * @param {Object} arg The callback argument object passed to the load function.
10792          */
10793         load : true,
10794         /**
10795          * @event loadexception
10796          * Fires if an Exception occurs during data retrieval.
10797          * @param {Object} This DataProxy object.
10798          * @param {Object} o The data object.
10799          * @param {Object} arg The callback argument object passed to the load function.
10800          * @param {Object} e The Exception.
10801          */
10802         loadexception : true
10803     });
10804     Roo.data.DataProxy.superclass.constructor.call(this);
10805 };
10806
10807 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
10808
10809     /**
10810      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
10811      */
10812 /*
10813  * Based on:
10814  * Ext JS Library 1.1.1
10815  * Copyright(c) 2006-2007, Ext JS, LLC.
10816  *
10817  * Originally Released Under LGPL - original licence link has changed is not relivant.
10818  *
10819  * Fork - LGPL
10820  * <script type="text/javascript">
10821  */
10822 /**
10823  * @class Roo.data.MemoryProxy
10824  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
10825  * to the Reader when its load method is called.
10826  * @constructor
10827  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
10828  */
10829 Roo.data.MemoryProxy = function(data){
10830     if (data.data) {
10831         data = data.data;
10832     }
10833     Roo.data.MemoryProxy.superclass.constructor.call(this);
10834     this.data = data;
10835 };
10836
10837 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
10838     /**
10839      * Load data from the requested source (in this case an in-memory
10840      * data object passed to the constructor), read the data object into
10841      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
10842      * process that block using the passed callback.
10843      * @param {Object} params This parameter is not used by the MemoryProxy class.
10844      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10845      * object into a block of Roo.data.Records.
10846      * @param {Function} callback The function into which to pass the block of Roo.data.records.
10847      * The function must be passed <ul>
10848      * <li>The Record block object</li>
10849      * <li>The "arg" argument from the load function</li>
10850      * <li>A boolean success indicator</li>
10851      * </ul>
10852      * @param {Object} scope The scope in which to call the callback
10853      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10854      */
10855     load : function(params, reader, callback, scope, arg){
10856         params = params || {};
10857         var result;
10858         try {
10859             result = reader.readRecords(this.data);
10860         }catch(e){
10861             this.fireEvent("loadexception", this, arg, null, e);
10862             callback.call(scope, null, arg, false);
10863             return;
10864         }
10865         callback.call(scope, result, arg, true);
10866     },
10867     
10868     // private
10869     update : function(params, records){
10870         
10871     }
10872 });/*
10873  * Based on:
10874  * Ext JS Library 1.1.1
10875  * Copyright(c) 2006-2007, Ext JS, LLC.
10876  *
10877  * Originally Released Under LGPL - original licence link has changed is not relivant.
10878  *
10879  * Fork - LGPL
10880  * <script type="text/javascript">
10881  */
10882 /**
10883  * @class Roo.data.HttpProxy
10884  * @extends Roo.data.DataProxy
10885  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
10886  * configured to reference a certain URL.<br><br>
10887  * <p>
10888  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
10889  * from which the running page was served.<br><br>
10890  * <p>
10891  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
10892  * <p>
10893  * Be aware that to enable the browser to parse an XML document, the server must set
10894  * the Content-Type header in the HTTP response to "text/xml".
10895  * @constructor
10896  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
10897  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
10898  * will be used to make the request.
10899  */
10900 Roo.data.HttpProxy = function(conn){
10901     Roo.data.HttpProxy.superclass.constructor.call(this);
10902     // is conn a conn config or a real conn?
10903     this.conn = conn;
10904     this.useAjax = !conn || !conn.events;
10905   
10906 };
10907
10908 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
10909     // thse are take from connection...
10910     
10911     /**
10912      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
10913      */
10914     /**
10915      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
10916      * extra parameters to each request made by this object. (defaults to undefined)
10917      */
10918     /**
10919      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
10920      *  to each request made by this object. (defaults to undefined)
10921      */
10922     /**
10923      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
10924      */
10925     /**
10926      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
10927      */
10928      /**
10929      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
10930      * @type Boolean
10931      */
10932   
10933
10934     /**
10935      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
10936      * @type Boolean
10937      */
10938     /**
10939      * Return the {@link Roo.data.Connection} object being used by this Proxy.
10940      * @return {Connection} The Connection object. This object may be used to subscribe to events on
10941      * a finer-grained basis than the DataProxy events.
10942      */
10943     getConnection : function(){
10944         return this.useAjax ? Roo.Ajax : this.conn;
10945     },
10946
10947     /**
10948      * Load data from the configured {@link Roo.data.Connection}, read the data object into
10949      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
10950      * process that block using the passed callback.
10951      * @param {Object} params An object containing properties which are to be used as HTTP parameters
10952      * for the request to the remote server.
10953      * @param {Roo.data.DataReader} reader The Reader object which converts the data
10954      * object into a block of Roo.data.Records.
10955      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
10956      * The function must be passed <ul>
10957      * <li>The Record block object</li>
10958      * <li>The "arg" argument from the load function</li>
10959      * <li>A boolean success indicator</li>
10960      * </ul>
10961      * @param {Object} scope The scope in which to call the callback
10962      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
10963      */
10964     load : function(params, reader, callback, scope, arg){
10965         if(this.fireEvent("beforeload", this, params) !== false){
10966             var  o = {
10967                 params : params || {},
10968                 request: {
10969                     callback : callback,
10970                     scope : scope,
10971                     arg : arg
10972                 },
10973                 reader: reader,
10974                 callback : this.loadResponse,
10975                 scope: this
10976             };
10977             if(this.useAjax){
10978                 Roo.applyIf(o, this.conn);
10979                 if(this.activeRequest){
10980                     Roo.Ajax.abort(this.activeRequest);
10981                 }
10982                 this.activeRequest = Roo.Ajax.request(o);
10983             }else{
10984                 this.conn.request(o);
10985             }
10986         }else{
10987             callback.call(scope||this, null, arg, false);
10988         }
10989     },
10990
10991     // private
10992     loadResponse : function(o, success, response){
10993         delete this.activeRequest;
10994         if(!success){
10995             this.fireEvent("loadexception", this, o, response);
10996             o.request.callback.call(o.request.scope, null, o.request.arg, false);
10997             return;
10998         }
10999         var result;
11000         try {
11001             result = o.reader.read(response);
11002         }catch(e){
11003             this.fireEvent("loadexception", this, o, response, e);
11004             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11005             return;
11006         }
11007         
11008         this.fireEvent("load", this, o, o.request.arg);
11009         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11010     },
11011
11012     // private
11013     update : function(dataSet){
11014
11015     },
11016
11017     // private
11018     updateResponse : function(dataSet){
11019
11020     }
11021 });/*
11022  * Based on:
11023  * Ext JS Library 1.1.1
11024  * Copyright(c) 2006-2007, Ext JS, LLC.
11025  *
11026  * Originally Released Under LGPL - original licence link has changed is not relivant.
11027  *
11028  * Fork - LGPL
11029  * <script type="text/javascript">
11030  */
11031
11032 /**
11033  * @class Roo.data.ScriptTagProxy
11034  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11035  * other than the originating domain of the running page.<br><br>
11036  * <p>
11037  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11038  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11039  * <p>
11040  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11041  * source code that is used as the source inside a &lt;script> tag.<br><br>
11042  * <p>
11043  * In order for the browser to process the returned data, the server must wrap the data object
11044  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11045  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11046  * depending on whether the callback name was passed:
11047  * <p>
11048  * <pre><code>
11049 boolean scriptTag = false;
11050 String cb = request.getParameter("callback");
11051 if (cb != null) {
11052     scriptTag = true;
11053     response.setContentType("text/javascript");
11054 } else {
11055     response.setContentType("application/x-json");
11056 }
11057 Writer out = response.getWriter();
11058 if (scriptTag) {
11059     out.write(cb + "(");
11060 }
11061 out.print(dataBlock.toJsonString());
11062 if (scriptTag) {
11063     out.write(");");
11064 }
11065 </pre></code>
11066  *
11067  * @constructor
11068  * @param {Object} config A configuration object.
11069  */
11070 Roo.data.ScriptTagProxy = function(config){
11071     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11072     Roo.apply(this, config);
11073     this.head = document.getElementsByTagName("head")[0];
11074 };
11075
11076 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11077
11078 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11079     /**
11080      * @cfg {String} url The URL from which to request the data object.
11081      */
11082     /**
11083      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11084      */
11085     timeout : 30000,
11086     /**
11087      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11088      * the server the name of the callback function set up by the load call to process the returned data object.
11089      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11090      * javascript output which calls this named function passing the data object as its only parameter.
11091      */
11092     callbackParam : "callback",
11093     /**
11094      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11095      * name to the request.
11096      */
11097     nocache : true,
11098
11099     /**
11100      * Load data from the configured URL, read the data object into
11101      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11102      * process that block using the passed callback.
11103      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11104      * for the request to the remote server.
11105      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11106      * object into a block of Roo.data.Records.
11107      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11108      * The function must be passed <ul>
11109      * <li>The Record block object</li>
11110      * <li>The "arg" argument from the load function</li>
11111      * <li>A boolean success indicator</li>
11112      * </ul>
11113      * @param {Object} scope The scope in which to call the callback
11114      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11115      */
11116     load : function(params, reader, callback, scope, arg){
11117         if(this.fireEvent("beforeload", this, params) !== false){
11118
11119             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11120
11121             var url = this.url;
11122             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11123             if(this.nocache){
11124                 url += "&_dc=" + (new Date().getTime());
11125             }
11126             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11127             var trans = {
11128                 id : transId,
11129                 cb : "stcCallback"+transId,
11130                 scriptId : "stcScript"+transId,
11131                 params : params,
11132                 arg : arg,
11133                 url : url,
11134                 callback : callback,
11135                 scope : scope,
11136                 reader : reader
11137             };
11138             var conn = this;
11139
11140             window[trans.cb] = function(o){
11141                 conn.handleResponse(o, trans);
11142             };
11143
11144             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11145
11146             if(this.autoAbort !== false){
11147                 this.abort();
11148             }
11149
11150             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11151
11152             var script = document.createElement("script");
11153             script.setAttribute("src", url);
11154             script.setAttribute("type", "text/javascript");
11155             script.setAttribute("id", trans.scriptId);
11156             this.head.appendChild(script);
11157
11158             this.trans = trans;
11159         }else{
11160             callback.call(scope||this, null, arg, false);
11161         }
11162     },
11163
11164     // private
11165     isLoading : function(){
11166         return this.trans ? true : false;
11167     },
11168
11169     /**
11170      * Abort the current server request.
11171      */
11172     abort : function(){
11173         if(this.isLoading()){
11174             this.destroyTrans(this.trans);
11175         }
11176     },
11177
11178     // private
11179     destroyTrans : function(trans, isLoaded){
11180         this.head.removeChild(document.getElementById(trans.scriptId));
11181         clearTimeout(trans.timeoutId);
11182         if(isLoaded){
11183             window[trans.cb] = undefined;
11184             try{
11185                 delete window[trans.cb];
11186             }catch(e){}
11187         }else{
11188             // if hasn't been loaded, wait for load to remove it to prevent script error
11189             window[trans.cb] = function(){
11190                 window[trans.cb] = undefined;
11191                 try{
11192                     delete window[trans.cb];
11193                 }catch(e){}
11194             };
11195         }
11196     },
11197
11198     // private
11199     handleResponse : function(o, trans){
11200         this.trans = false;
11201         this.destroyTrans(trans, true);
11202         var result;
11203         try {
11204             result = trans.reader.readRecords(o);
11205         }catch(e){
11206             this.fireEvent("loadexception", this, o, trans.arg, e);
11207             trans.callback.call(trans.scope||window, null, trans.arg, false);
11208             return;
11209         }
11210         this.fireEvent("load", this, o, trans.arg);
11211         trans.callback.call(trans.scope||window, result, trans.arg, true);
11212     },
11213
11214     // private
11215     handleFailure : function(trans){
11216         this.trans = false;
11217         this.destroyTrans(trans, false);
11218         this.fireEvent("loadexception", this, null, trans.arg);
11219         trans.callback.call(trans.scope||window, null, trans.arg, false);
11220     }
11221 });/*
11222  * Based on:
11223  * Ext JS Library 1.1.1
11224  * Copyright(c) 2006-2007, Ext JS, LLC.
11225  *
11226  * Originally Released Under LGPL - original licence link has changed is not relivant.
11227  *
11228  * Fork - LGPL
11229  * <script type="text/javascript">
11230  */
11231
11232 /**
11233  * @class Roo.data.JsonReader
11234  * @extends Roo.data.DataReader
11235  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11236  * based on mappings in a provided Roo.data.Record constructor.
11237  * 
11238  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11239  * in the reply previously. 
11240  * 
11241  * <p>
11242  * Example code:
11243  * <pre><code>
11244 var RecordDef = Roo.data.Record.create([
11245     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11246     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11247 ]);
11248 var myReader = new Roo.data.JsonReader({
11249     totalProperty: "results",    // The property which contains the total dataset size (optional)
11250     root: "rows",                // The property which contains an Array of row objects
11251     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11252 }, RecordDef);
11253 </code></pre>
11254  * <p>
11255  * This would consume a JSON file like this:
11256  * <pre><code>
11257 { 'results': 2, 'rows': [
11258     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11259     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11260 }
11261 </code></pre>
11262  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11263  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11264  * paged from the remote server.
11265  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11266  * @cfg {String} root name of the property which contains the Array of row objects.
11267  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11268  * @cfg {Array} fields Array of field definition objects
11269  * @constructor
11270  * Create a new JsonReader
11271  * @param {Object} meta Metadata configuration options
11272  * @param {Object} recordType Either an Array of field definition objects,
11273  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11274  */
11275 Roo.data.JsonReader = function(meta, recordType){
11276     
11277     meta = meta || {};
11278     // set some defaults:
11279     Roo.applyIf(meta, {
11280         totalProperty: 'total',
11281         successProperty : 'success',
11282         root : 'data',
11283         id : 'id'
11284     });
11285     
11286     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11287 };
11288 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11289     
11290     /**
11291      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11292      * Used by Store query builder to append _requestMeta to params.
11293      * 
11294      */
11295     metaFromRemote : false,
11296     /**
11297      * This method is only used by a DataProxy which has retrieved data from a remote server.
11298      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11299      * @return {Object} data A data block which is used by an Roo.data.Store object as
11300      * a cache of Roo.data.Records.
11301      */
11302     read : function(response){
11303         var json = response.responseText;
11304        
11305         var o = /* eval:var:o */ eval("("+json+")");
11306         if(!o) {
11307             throw {message: "JsonReader.read: Json object not found"};
11308         }
11309         
11310         if(o.metaData){
11311             
11312             delete this.ef;
11313             this.metaFromRemote = true;
11314             this.meta = o.metaData;
11315             this.recordType = Roo.data.Record.create(o.metaData.fields);
11316             this.onMetaChange(this.meta, this.recordType, o);
11317         }
11318         return this.readRecords(o);
11319     },
11320
11321     // private function a store will implement
11322     onMetaChange : function(meta, recordType, o){
11323
11324     },
11325
11326     /**
11327          * @ignore
11328          */
11329     simpleAccess: function(obj, subsc) {
11330         return obj[subsc];
11331     },
11332
11333         /**
11334          * @ignore
11335          */
11336     getJsonAccessor: function(){
11337         var re = /[\[\.]/;
11338         return function(expr) {
11339             try {
11340                 return(re.test(expr))
11341                     ? new Function("obj", "return obj." + expr)
11342                     : function(obj){
11343                         return obj[expr];
11344                     };
11345             } catch(e){}
11346             return Roo.emptyFn;
11347         };
11348     }(),
11349
11350     /**
11351      * Create a data block containing Roo.data.Records from an XML document.
11352      * @param {Object} o An object which contains an Array of row objects in the property specified
11353      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11354      * which contains the total size of the dataset.
11355      * @return {Object} data A data block which is used by an Roo.data.Store object as
11356      * a cache of Roo.data.Records.
11357      */
11358     readRecords : function(o){
11359         /**
11360          * After any data loads, the raw JSON data is available for further custom processing.
11361          * @type Object
11362          */
11363         this.o = o;
11364         var s = this.meta, Record = this.recordType,
11365             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11366
11367 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11368         if (!this.ef) {
11369             if(s.totalProperty) {
11370                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11371                 }
11372                 if(s.successProperty) {
11373                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11374                 }
11375                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11376                 if (s.id) {
11377                         var g = this.getJsonAccessor(s.id);
11378                         this.getId = function(rec) {
11379                                 var r = g(rec);  
11380                                 return (r === undefined || r === "") ? null : r;
11381                         };
11382                 } else {
11383                         this.getId = function(){return null;};
11384                 }
11385             this.ef = [];
11386             for(var jj = 0; jj < fl; jj++){
11387                 f = fi[jj];
11388                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11389                 this.ef[jj] = this.getJsonAccessor(map);
11390             }
11391         }
11392
11393         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11394         if(s.totalProperty){
11395             var vt = parseInt(this.getTotal(o), 10);
11396             if(!isNaN(vt)){
11397                 totalRecords = vt;
11398             }
11399         }
11400         if(s.successProperty){
11401             var vs = this.getSuccess(o);
11402             if(vs === false || vs === 'false'){
11403                 success = false;
11404             }
11405         }
11406         var records = [];
11407         for(var i = 0; i < c; i++){
11408                 var n = root[i];
11409             var values = {};
11410             var id = this.getId(n);
11411             for(var j = 0; j < fl; j++){
11412                 f = fi[j];
11413             var v = this.ef[j](n);
11414             if (!f.convert) {
11415                 Roo.log('missing convert for ' + f.name);
11416                 Roo.log(f);
11417                 continue;
11418             }
11419             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11420             }
11421             var record = new Record(values, id);
11422             record.json = n;
11423             records[i] = record;
11424         }
11425         return {
11426             raw : o,
11427             success : success,
11428             records : records,
11429             totalRecords : totalRecords
11430         };
11431     }
11432 });/*
11433  * Based on:
11434  * Ext JS Library 1.1.1
11435  * Copyright(c) 2006-2007, Ext JS, LLC.
11436  *
11437  * Originally Released Under LGPL - original licence link has changed is not relivant.
11438  *
11439  * Fork - LGPL
11440  * <script type="text/javascript">
11441  */
11442
11443 /**
11444  * @class Roo.data.ArrayReader
11445  * @extends Roo.data.DataReader
11446  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11447  * Each element of that Array represents a row of data fields. The
11448  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11449  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11450  * <p>
11451  * Example code:.
11452  * <pre><code>
11453 var RecordDef = Roo.data.Record.create([
11454     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11455     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11456 ]);
11457 var myReader = new Roo.data.ArrayReader({
11458     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11459 }, RecordDef);
11460 </code></pre>
11461  * <p>
11462  * This would consume an Array like this:
11463  * <pre><code>
11464 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11465   </code></pre>
11466  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11467  * @constructor
11468  * Create a new JsonReader
11469  * @param {Object} meta Metadata configuration options.
11470  * @param {Object} recordType Either an Array of field definition objects
11471  * as specified to {@link Roo.data.Record#create},
11472  * or an {@link Roo.data.Record} object
11473  * created using {@link Roo.data.Record#create}.
11474  */
11475 Roo.data.ArrayReader = function(meta, recordType){
11476     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11477 };
11478
11479 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11480     /**
11481      * Create a data block containing Roo.data.Records from an XML document.
11482      * @param {Object} o An Array of row objects which represents the dataset.
11483      * @return {Object} data A data block which is used by an Roo.data.Store object as
11484      * a cache of Roo.data.Records.
11485      */
11486     readRecords : function(o){
11487         var sid = this.meta ? this.meta.id : null;
11488         var recordType = this.recordType, fields = recordType.prototype.fields;
11489         var records = [];
11490         var root = o;
11491             for(var i = 0; i < root.length; i++){
11492                     var n = root[i];
11493                 var values = {};
11494                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11495                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11496                 var f = fields.items[j];
11497                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11498                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11499                 v = f.convert(v);
11500                 values[f.name] = v;
11501             }
11502                 var record = new recordType(values, id);
11503                 record.json = n;
11504                 records[records.length] = record;
11505             }
11506             return {
11507                 records : records,
11508                 totalRecords : records.length
11509             };
11510     }
11511 });/*
11512  * - LGPL
11513  * * 
11514  */
11515
11516 /**
11517  * @class Roo.bootstrap.ComboBox
11518  * @extends Roo.bootstrap.TriggerField
11519  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11520  * @cfg {Boolean} append (true|false) default false
11521  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11522  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11523  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11524  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11525  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11526  * @cfg {Boolean} animate default true
11527  * @cfg {Boolean} emptyResultText only for touch device
11528  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11529  * @constructor
11530  * Create a new ComboBox.
11531  * @param {Object} config Configuration options
11532  */
11533 Roo.bootstrap.ComboBox = function(config){
11534     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11535     this.addEvents({
11536         /**
11537          * @event expand
11538          * Fires when the dropdown list is expanded
11539              * @param {Roo.bootstrap.ComboBox} combo This combo box
11540              */
11541         'expand' : true,
11542         /**
11543          * @event collapse
11544          * Fires when the dropdown list is collapsed
11545              * @param {Roo.bootstrap.ComboBox} combo This combo box
11546              */
11547         'collapse' : true,
11548         /**
11549          * @event beforeselect
11550          * Fires before a list item is selected. Return false to cancel the selection.
11551              * @param {Roo.bootstrap.ComboBox} combo This combo box
11552              * @param {Roo.data.Record} record The data record returned from the underlying store
11553              * @param {Number} index The index of the selected item in the dropdown list
11554              */
11555         'beforeselect' : true,
11556         /**
11557          * @event select
11558          * Fires when a list item is selected
11559              * @param {Roo.bootstrap.ComboBox} combo This combo box
11560              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11561              * @param {Number} index The index of the selected item in the dropdown list
11562              */
11563         'select' : true,
11564         /**
11565          * @event beforequery
11566          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11567          * The event object passed has these properties:
11568              * @param {Roo.bootstrap.ComboBox} combo This combo box
11569              * @param {String} query The query
11570              * @param {Boolean} forceAll true to force "all" query
11571              * @param {Boolean} cancel true to cancel the query
11572              * @param {Object} e The query event object
11573              */
11574         'beforequery': true,
11575          /**
11576          * @event add
11577          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11578              * @param {Roo.bootstrap.ComboBox} combo This combo box
11579              */
11580         'add' : true,
11581         /**
11582          * @event edit
11583          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11584              * @param {Roo.bootstrap.ComboBox} combo This combo box
11585              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11586              */
11587         'edit' : true,
11588         /**
11589          * @event remove
11590          * Fires when the remove value from the combobox array
11591              * @param {Roo.bootstrap.ComboBox} combo This combo box
11592              */
11593         'remove' : true,
11594         /**
11595          * @event specialfilter
11596          * Fires when specialfilter
11597             * @param {Roo.bootstrap.ComboBox} combo This combo box
11598             */
11599         'specialfilter' : true,
11600         /**
11601          * @event tick
11602          * Fires when tick the element
11603             * @param {Roo.bootstrap.ComboBox} combo This combo box
11604             */
11605         'tick' : true,
11606         /**
11607          * @event touchviewdisplay
11608          * Fires when touch view require special display (default is using displayField)
11609             * @param {Roo.bootstrap.ComboBox} combo This combo box
11610             * @param {Object} cfg set html .
11611             */
11612         'touchviewdisplay' : true
11613         
11614     });
11615     
11616     this.item = [];
11617     this.tickItems = [];
11618     
11619     this.selectedIndex = -1;
11620     if(this.mode == 'local'){
11621         if(config.queryDelay === undefined){
11622             this.queryDelay = 10;
11623         }
11624         if(config.minChars === undefined){
11625             this.minChars = 0;
11626         }
11627     }
11628 };
11629
11630 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11631      
11632     /**
11633      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11634      * rendering into an Roo.Editor, defaults to false)
11635      */
11636     /**
11637      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11638      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11639      */
11640     /**
11641      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11642      */
11643     /**
11644      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11645      * the dropdown list (defaults to undefined, with no header element)
11646      */
11647
11648      /**
11649      * @cfg {String/Roo.Template} tpl The template to use to render the output
11650      */
11651      
11652      /**
11653      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11654      */
11655     listWidth: undefined,
11656     /**
11657      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11658      * mode = 'remote' or 'text' if mode = 'local')
11659      */
11660     displayField: undefined,
11661     
11662     /**
11663      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11664      * mode = 'remote' or 'value' if mode = 'local'). 
11665      * Note: use of a valueField requires the user make a selection
11666      * in order for a value to be mapped.
11667      */
11668     valueField: undefined,
11669     
11670     
11671     /**
11672      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11673      * field's data value (defaults to the underlying DOM element's name)
11674      */
11675     hiddenName: undefined,
11676     /**
11677      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11678      */
11679     listClass: '',
11680     /**
11681      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11682      */
11683     selectedClass: 'active',
11684     
11685     /**
11686      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11687      */
11688     shadow:'sides',
11689     /**
11690      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11691      * anchor positions (defaults to 'tl-bl')
11692      */
11693     listAlign: 'tl-bl?',
11694     /**
11695      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11696      */
11697     maxHeight: 300,
11698     /**
11699      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11700      * query specified by the allQuery config option (defaults to 'query')
11701      */
11702     triggerAction: 'query',
11703     /**
11704      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11705      * (defaults to 4, does not apply if editable = false)
11706      */
11707     minChars : 4,
11708     /**
11709      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11710      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11711      */
11712     typeAhead: false,
11713     /**
11714      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11715      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11716      */
11717     queryDelay: 500,
11718     /**
11719      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11720      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11721      */
11722     pageSize: 0,
11723     /**
11724      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11725      * when editable = true (defaults to false)
11726      */
11727     selectOnFocus:false,
11728     /**
11729      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11730      */
11731     queryParam: 'query',
11732     /**
11733      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
11734      * when mode = 'remote' (defaults to 'Loading...')
11735      */
11736     loadingText: 'Loading...',
11737     /**
11738      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
11739      */
11740     resizable: false,
11741     /**
11742      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
11743      */
11744     handleHeight : 8,
11745     /**
11746      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
11747      * traditional select (defaults to true)
11748      */
11749     editable: true,
11750     /**
11751      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
11752      */
11753     allQuery: '',
11754     /**
11755      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
11756      */
11757     mode: 'remote',
11758     /**
11759      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
11760      * listWidth has a higher value)
11761      */
11762     minListWidth : 70,
11763     /**
11764      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
11765      * allow the user to set arbitrary text into the field (defaults to false)
11766      */
11767     forceSelection:false,
11768     /**
11769      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
11770      * if typeAhead = true (defaults to 250)
11771      */
11772     typeAheadDelay : 250,
11773     /**
11774      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
11775      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
11776      */
11777     valueNotFoundText : undefined,
11778     /**
11779      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
11780      */
11781     blockFocus : false,
11782     
11783     /**
11784      * @cfg {Boolean} disableClear Disable showing of clear button.
11785      */
11786     disableClear : false,
11787     /**
11788      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
11789      */
11790     alwaysQuery : false,
11791     
11792     /**
11793      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
11794      */
11795     multiple : false,
11796     
11797     /**
11798      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
11799      */
11800     invalidClass : "has-warning",
11801     
11802     /**
11803      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
11804      */
11805     validClass : "has-success",
11806     
11807     /**
11808      * @cfg {Boolean} specialFilter (true|false) special filter default false
11809      */
11810     specialFilter : false,
11811     
11812     /**
11813      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
11814      */
11815     mobileTouchView : true,
11816     
11817     //private
11818     addicon : false,
11819     editicon: false,
11820     
11821     page: 0,
11822     hasQuery: false,
11823     append: false,
11824     loadNext: false,
11825     autoFocus : true,
11826     tickable : false,
11827     btnPosition : 'right',
11828     triggerList : true,
11829     showToggleBtn : true,
11830     animate : true,
11831     emptyResultText: 'Empty',
11832     triggerText : 'Select',
11833     
11834     // element that contains real text value.. (when hidden is used..)
11835     
11836     getAutoCreate : function()
11837     {
11838         var cfg = false;
11839         
11840         /*
11841          * Touch Devices
11842          */
11843         
11844         if(Roo.isTouch && this.mobileTouchView){
11845             cfg = this.getAutoCreateTouchView();
11846             return cfg;;
11847         }
11848         
11849         /*
11850          *  Normal ComboBox
11851          */
11852         if(!this.tickable){
11853             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
11854             return cfg;
11855         }
11856         
11857         /*
11858          *  ComboBox with tickable selections
11859          */
11860              
11861         var align = this.labelAlign || this.parentLabelAlign();
11862         
11863         cfg = {
11864             cls : 'form-group roo-combobox-tickable' //input-group
11865         };
11866         
11867         var buttons = {
11868             tag : 'div',
11869             cls : 'tickable-buttons',
11870             cn : [
11871                 {
11872                     tag : 'button',
11873                     type : 'button',
11874                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
11875                     html : this.triggerText
11876                 },
11877                 {
11878                     tag : 'button',
11879                     type : 'button',
11880                     name : 'ok',
11881                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
11882                     html : 'Done'
11883                 },
11884                 {
11885                     tag : 'button',
11886                     type : 'button',
11887                     name : 'cancel',
11888                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
11889                     html : 'Cancel'
11890                 }
11891             ]
11892         };
11893         
11894         if(this.editable){
11895             buttons.cn.unshift({
11896                 tag: 'input',
11897                 cls: 'select2-search-field-input'
11898             });
11899         }
11900         
11901         var _this = this;
11902         
11903         Roo.each(buttons.cn, function(c){
11904             if (_this.size) {
11905                 c.cls += ' btn-' + _this.size;
11906             }
11907
11908             if (_this.disabled) {
11909                 c.disabled = true;
11910             }
11911         });
11912         
11913         var box = {
11914             tag: 'div',
11915             cn: [
11916                 {
11917                     tag: 'input',
11918                     type : 'hidden',
11919                     cls: 'form-hidden-field'
11920                 },
11921                 {
11922                     tag: 'ul',
11923                     cls: 'select2-choices',
11924                     cn:[
11925                         {
11926                             tag: 'li',
11927                             cls: 'select2-search-field',
11928                             cn: [
11929
11930                                 buttons
11931                             ]
11932                         }
11933                     ]
11934                 }
11935             ]
11936         };
11937         
11938         var combobox = {
11939             cls: 'select2-container input-group select2-container-multi',
11940             cn: [
11941                 box
11942 //                {
11943 //                    tag: 'ul',
11944 //                    cls: 'typeahead typeahead-long dropdown-menu',
11945 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
11946 //                }
11947             ]
11948         };
11949         
11950         if(this.hasFeedback && !this.allowBlank){
11951             
11952             var feedback = {
11953                 tag: 'span',
11954                 cls: 'glyphicon form-control-feedback'
11955             };
11956
11957             combobox.cn.push(feedback);
11958         }
11959         
11960         if (align ==='left' && this.fieldLabel.length) {
11961             
11962 //                Roo.log("left and has label");
11963                 cfg.cn = [
11964                     
11965                     {
11966                         tag: 'label',
11967                         'for' :  id,
11968                         cls : 'control-label col-sm-' + this.labelWidth,
11969                         html : this.fieldLabel
11970                         
11971                     },
11972                     {
11973                         cls : "col-sm-" + (12 - this.labelWidth), 
11974                         cn: [
11975                             combobox
11976                         ]
11977                     }
11978                     
11979                 ];
11980         } else if ( this.fieldLabel.length) {
11981 //                Roo.log(" label");
11982                  cfg.cn = [
11983                    
11984                     {
11985                         tag: 'label',
11986                         //cls : 'input-group-addon',
11987                         html : this.fieldLabel
11988                         
11989                     },
11990                     
11991                     combobox
11992                     
11993                 ];
11994
11995         } else {
11996             
11997 //                Roo.log(" no label && no align");
11998                 cfg = combobox
11999                      
12000                 
12001         }
12002          
12003         var settings=this;
12004         ['xs','sm','md','lg'].map(function(size){
12005             if (settings[size]) {
12006                 cfg.cls += ' col-' + size + '-' + settings[size];
12007             }
12008         });
12009         
12010         return cfg;
12011         
12012     },
12013     
12014     _initEventsCalled : false,
12015     
12016     // private
12017     initEvents: function()
12018     {
12019         
12020         if (this._initEventsCalled) { // as we call render... prevent looping...
12021             return;
12022         }
12023         this._initEventsCalled = true;
12024         
12025         if (!this.store) {
12026             throw "can not find store for combo";
12027         }
12028         
12029         this.store = Roo.factory(this.store, Roo.data);
12030         
12031         // if we are building from html. then this element is so complex, that we can not really
12032         // use the rendered HTML.
12033         // so we have to trash and replace the previous code.
12034         if (Roo.XComponent.build_from_html) {
12035             
12036             // remove this element....
12037             var e = this.el.dom, k=0;
12038             while (e ) { e = e.previousSibling;  ++k;}
12039
12040             this.el.remove();
12041             
12042             this.el=false;
12043             this.rendered = false;
12044             
12045             this.render(this.parent().getChildContainer(true), k);
12046             
12047             
12048             
12049         }
12050         
12051         
12052         /*
12053          * Touch Devices
12054          */
12055         
12056         if(Roo.isTouch && this.mobileTouchView){
12057             this.initTouchView();
12058             return;
12059         }
12060         
12061         if(this.tickable){
12062             this.initTickableEvents();
12063             return;
12064         }
12065         
12066         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12067         
12068         if(this.hiddenName){
12069             
12070             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12071             
12072             this.hiddenField.dom.value =
12073                 this.hiddenValue !== undefined ? this.hiddenValue :
12074                 this.value !== undefined ? this.value : '';
12075
12076             // prevent input submission
12077             this.el.dom.removeAttribute('name');
12078             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12079              
12080              
12081         }
12082         //if(Roo.isGecko){
12083         //    this.el.dom.setAttribute('autocomplete', 'off');
12084         //}
12085         
12086         var cls = 'x-combo-list';
12087         
12088         //this.list = new Roo.Layer({
12089         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12090         //});
12091         
12092         var _this = this;
12093         
12094         (function(){
12095             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12096             _this.list.setWidth(lw);
12097         }).defer(100);
12098         
12099         this.list.on('mouseover', this.onViewOver, this);
12100         this.list.on('mousemove', this.onViewMove, this);
12101         
12102         this.list.on('scroll', this.onViewScroll, this);
12103         
12104         /*
12105         this.list.swallowEvent('mousewheel');
12106         this.assetHeight = 0;
12107
12108         if(this.title){
12109             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12110             this.assetHeight += this.header.getHeight();
12111         }
12112
12113         this.innerList = this.list.createChild({cls:cls+'-inner'});
12114         this.innerList.on('mouseover', this.onViewOver, this);
12115         this.innerList.on('mousemove', this.onViewMove, this);
12116         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12117         
12118         if(this.allowBlank && !this.pageSize && !this.disableClear){
12119             this.footer = this.list.createChild({cls:cls+'-ft'});
12120             this.pageTb = new Roo.Toolbar(this.footer);
12121            
12122         }
12123         if(this.pageSize){
12124             this.footer = this.list.createChild({cls:cls+'-ft'});
12125             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12126                     {pageSize: this.pageSize});
12127             
12128         }
12129         
12130         if (this.pageTb && this.allowBlank && !this.disableClear) {
12131             var _this = this;
12132             this.pageTb.add(new Roo.Toolbar.Fill(), {
12133                 cls: 'x-btn-icon x-btn-clear',
12134                 text: '&#160;',
12135                 handler: function()
12136                 {
12137                     _this.collapse();
12138                     _this.clearValue();
12139                     _this.onSelect(false, -1);
12140                 }
12141             });
12142         }
12143         if (this.footer) {
12144             this.assetHeight += this.footer.getHeight();
12145         }
12146         */
12147             
12148         if(!this.tpl){
12149             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12150         }
12151
12152         this.view = new Roo.View(this.list, this.tpl, {
12153             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12154         });
12155         //this.view.wrapEl.setDisplayed(false);
12156         this.view.on('click', this.onViewClick, this);
12157         
12158         
12159         
12160         this.store.on('beforeload', this.onBeforeLoad, this);
12161         this.store.on('load', this.onLoad, this);
12162         this.store.on('loadexception', this.onLoadException, this);
12163         /*
12164         if(this.resizable){
12165             this.resizer = new Roo.Resizable(this.list,  {
12166                pinned:true, handles:'se'
12167             });
12168             this.resizer.on('resize', function(r, w, h){
12169                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12170                 this.listWidth = w;
12171                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12172                 this.restrictHeight();
12173             }, this);
12174             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12175         }
12176         */
12177         if(!this.editable){
12178             this.editable = true;
12179             this.setEditable(false);
12180         }
12181         
12182         /*
12183         
12184         if (typeof(this.events.add.listeners) != 'undefined') {
12185             
12186             this.addicon = this.wrap.createChild(
12187                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12188        
12189             this.addicon.on('click', function(e) {
12190                 this.fireEvent('add', this);
12191             }, this);
12192         }
12193         if (typeof(this.events.edit.listeners) != 'undefined') {
12194             
12195             this.editicon = this.wrap.createChild(
12196                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12197             if (this.addicon) {
12198                 this.editicon.setStyle('margin-left', '40px');
12199             }
12200             this.editicon.on('click', function(e) {
12201                 
12202                 // we fire even  if inothing is selected..
12203                 this.fireEvent('edit', this, this.lastData );
12204                 
12205             }, this);
12206         }
12207         */
12208         
12209         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12210             "up" : function(e){
12211                 this.inKeyMode = true;
12212                 this.selectPrev();
12213             },
12214
12215             "down" : function(e){
12216                 if(!this.isExpanded()){
12217                     this.onTriggerClick();
12218                 }else{
12219                     this.inKeyMode = true;
12220                     this.selectNext();
12221                 }
12222             },
12223
12224             "enter" : function(e){
12225 //                this.onViewClick();
12226                 //return true;
12227                 this.collapse();
12228                 
12229                 if(this.fireEvent("specialkey", this, e)){
12230                     this.onViewClick(false);
12231                 }
12232                 
12233                 return true;
12234             },
12235
12236             "esc" : function(e){
12237                 this.collapse();
12238             },
12239
12240             "tab" : function(e){
12241                 this.collapse();
12242                 
12243                 if(this.fireEvent("specialkey", this, e)){
12244                     this.onViewClick(false);
12245                 }
12246                 
12247                 return true;
12248             },
12249
12250             scope : this,
12251
12252             doRelay : function(foo, bar, hname){
12253                 if(hname == 'down' || this.scope.isExpanded()){
12254                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12255                 }
12256                 return true;
12257             },
12258
12259             forceKeyDown: true
12260         });
12261         
12262         
12263         this.queryDelay = Math.max(this.queryDelay || 10,
12264                 this.mode == 'local' ? 10 : 250);
12265         
12266         
12267         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12268         
12269         if(this.typeAhead){
12270             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12271         }
12272         if(this.editable !== false){
12273             this.inputEl().on("keyup", this.onKeyUp, this);
12274         }
12275         if(this.forceSelection){
12276             this.inputEl().on('blur', this.doForce, this);
12277         }
12278         
12279         if(this.multiple){
12280             this.choices = this.el.select('ul.select2-choices', true).first();
12281             this.searchField = this.el.select('ul li.select2-search-field', true).first();
12282         }
12283     },
12284     
12285     initTickableEvents: function()
12286     {   
12287         this.createList();
12288         
12289         if(this.hiddenName){
12290             
12291             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12292             
12293             this.hiddenField.dom.value =
12294                 this.hiddenValue !== undefined ? this.hiddenValue :
12295                 this.value !== undefined ? this.value : '';
12296
12297             // prevent input submission
12298             this.el.dom.removeAttribute('name');
12299             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12300              
12301              
12302         }
12303         
12304 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12305         
12306         this.choices = this.el.select('ul.select2-choices', true).first();
12307         this.searchField = this.el.select('ul li.select2-search-field', true).first();
12308         if(this.triggerList){
12309             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12310         }
12311          
12312         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12313         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12314         
12315         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12316         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12317         
12318         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12319         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12320         
12321         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12322         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12323         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12324         
12325         this.okBtn.hide();
12326         this.cancelBtn.hide();
12327         
12328         var _this = this;
12329         
12330         (function(){
12331             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12332             _this.list.setWidth(lw);
12333         }).defer(100);
12334         
12335         this.list.on('mouseover', this.onViewOver, this);
12336         this.list.on('mousemove', this.onViewMove, this);
12337         
12338         this.list.on('scroll', this.onViewScroll, this);
12339         
12340         if(!this.tpl){
12341             this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
12342         }
12343
12344         this.view = new Roo.View(this.list, this.tpl, {
12345             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12346         });
12347         
12348         //this.view.wrapEl.setDisplayed(false);
12349         this.view.on('click', this.onViewClick, this);
12350         
12351         
12352         
12353         this.store.on('beforeload', this.onBeforeLoad, this);
12354         this.store.on('load', this.onLoad, this);
12355         this.store.on('loadexception', this.onLoadException, this);
12356         
12357         if(this.editable){
12358             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12359                 "up" : function(e){
12360                     this.inKeyMode = true;
12361                     this.selectPrev();
12362                 },
12363
12364                 "down" : function(e){
12365                     this.inKeyMode = true;
12366                     this.selectNext();
12367                 },
12368
12369                 "enter" : function(e){
12370                     if(this.fireEvent("specialkey", this, e)){
12371                         this.onViewClick(false);
12372                     }
12373                     
12374                     return true;
12375                 },
12376
12377                 "esc" : function(e){
12378                     this.onTickableFooterButtonClick(e, false, false);
12379                 },
12380
12381                 "tab" : function(e){
12382                     this.fireEvent("specialkey", this, e);
12383                     
12384                     this.onTickableFooterButtonClick(e, false, false);
12385                     
12386                     return true;
12387                 },
12388
12389                 scope : this,
12390
12391                 doRelay : function(e, fn, key){
12392                     if(this.scope.isExpanded()){
12393                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12394                     }
12395                     return true;
12396                 },
12397
12398                 forceKeyDown: true
12399             });
12400         }
12401         
12402         this.queryDelay = Math.max(this.queryDelay || 10,
12403                 this.mode == 'local' ? 10 : 250);
12404         
12405         
12406         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12407         
12408         if(this.typeAhead){
12409             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12410         }
12411         
12412         if(this.editable !== false){
12413             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12414         }
12415         
12416     },
12417
12418     onDestroy : function(){
12419         if(this.view){
12420             this.view.setStore(null);
12421             this.view.el.removeAllListeners();
12422             this.view.el.remove();
12423             this.view.purgeListeners();
12424         }
12425         if(this.list){
12426             this.list.dom.innerHTML  = '';
12427         }
12428         
12429         if(this.store){
12430             this.store.un('beforeload', this.onBeforeLoad, this);
12431             this.store.un('load', this.onLoad, this);
12432             this.store.un('loadexception', this.onLoadException, this);
12433         }
12434         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12435     },
12436
12437     // private
12438     fireKey : function(e){
12439         if(e.isNavKeyPress() && !this.list.isVisible()){
12440             this.fireEvent("specialkey", this, e);
12441         }
12442     },
12443
12444     // private
12445     onResize: function(w, h){
12446 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12447 //        
12448 //        if(typeof w != 'number'){
12449 //            // we do not handle it!?!?
12450 //            return;
12451 //        }
12452 //        var tw = this.trigger.getWidth();
12453 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12454 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12455 //        var x = w - tw;
12456 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12457 //            
12458 //        //this.trigger.setStyle('left', x+'px');
12459 //        
12460 //        if(this.list && this.listWidth === undefined){
12461 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12462 //            this.list.setWidth(lw);
12463 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12464 //        }
12465         
12466     
12467         
12468     },
12469
12470     /**
12471      * Allow or prevent the user from directly editing the field text.  If false is passed,
12472      * the user will only be able to select from the items defined in the dropdown list.  This method
12473      * is the runtime equivalent of setting the 'editable' config option at config time.
12474      * @param {Boolean} value True to allow the user to directly edit the field text
12475      */
12476     setEditable : function(value){
12477         if(value == this.editable){
12478             return;
12479         }
12480         this.editable = value;
12481         if(!value){
12482             this.inputEl().dom.setAttribute('readOnly', true);
12483             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12484             this.inputEl().addClass('x-combo-noedit');
12485         }else{
12486             this.inputEl().dom.setAttribute('readOnly', false);
12487             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12488             this.inputEl().removeClass('x-combo-noedit');
12489         }
12490     },
12491
12492     // private
12493     
12494     onBeforeLoad : function(combo,opts){
12495         if(!this.hasFocus){
12496             return;
12497         }
12498          if (!opts.add) {
12499             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12500          }
12501         this.restrictHeight();
12502         this.selectedIndex = -1;
12503     },
12504
12505     // private
12506     onLoad : function(){
12507         
12508         this.hasQuery = false;
12509         
12510         if(!this.hasFocus){
12511             return;
12512         }
12513         
12514         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12515             this.loading.hide();
12516         }
12517              
12518         if(this.store.getCount() > 0){
12519             this.expand();
12520             this.restrictHeight();
12521             if(this.lastQuery == this.allQuery){
12522                 if(this.editable && !this.tickable){
12523                     this.inputEl().dom.select();
12524                 }
12525                 
12526                 if(
12527                     !this.selectByValue(this.value, true) &&
12528                     this.autoFocus && 
12529                     (
12530                         !this.store.lastOptions ||
12531                         typeof(this.store.lastOptions.add) == 'undefined' || 
12532                         this.store.lastOptions.add != true
12533                     )
12534                 ){
12535                     this.select(0, true);
12536                 }
12537             }else{
12538                 if(this.autoFocus){
12539                     this.selectNext();
12540                 }
12541                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12542                     this.taTask.delay(this.typeAheadDelay);
12543                 }
12544             }
12545         }else{
12546             this.onEmptyResults();
12547         }
12548         
12549         //this.el.focus();
12550     },
12551     // private
12552     onLoadException : function()
12553     {
12554         this.hasQuery = false;
12555         
12556         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12557             this.loading.hide();
12558         }
12559         
12560         if(this.tickable && this.editable){
12561             return;
12562         }
12563         
12564         this.collapse();
12565         // only causes errors at present
12566         //Roo.log(this.store.reader.jsonData);
12567         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12568             // fixme
12569             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12570         //}
12571         
12572         
12573     },
12574     // private
12575     onTypeAhead : function(){
12576         if(this.store.getCount() > 0){
12577             var r = this.store.getAt(0);
12578             var newValue = r.data[this.displayField];
12579             var len = newValue.length;
12580             var selStart = this.getRawValue().length;
12581             
12582             if(selStart != len){
12583                 this.setRawValue(newValue);
12584                 this.selectText(selStart, newValue.length);
12585             }
12586         }
12587     },
12588
12589     // private
12590     onSelect : function(record, index){
12591         
12592         if(this.fireEvent('beforeselect', this, record, index) !== false){
12593         
12594             this.setFromData(index > -1 ? record.data : false);
12595             
12596             this.collapse();
12597             this.fireEvent('select', this, record, index);
12598         }
12599     },
12600
12601     /**
12602      * Returns the currently selected field value or empty string if no value is set.
12603      * @return {String} value The selected value
12604      */
12605     getValue : function(){
12606         
12607         if(this.multiple){
12608             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12609         }
12610         
12611         if(this.valueField){
12612             return typeof this.value != 'undefined' ? this.value : '';
12613         }else{
12614             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12615         }
12616     },
12617
12618     /**
12619      * Clears any text/value currently set in the field
12620      */
12621     clearValue : function(){
12622         if(this.hiddenField){
12623             this.hiddenField.dom.value = '';
12624         }
12625         this.value = '';
12626         this.setRawValue('');
12627         this.lastSelectionText = '';
12628         this.lastData = false;
12629         
12630         var close = this.closeTriggerEl();
12631         
12632         if(close){
12633             close.hide();
12634         }
12635         
12636     },
12637
12638     /**
12639      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12640      * will be displayed in the field.  If the value does not match the data value of an existing item,
12641      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12642      * Otherwise the field will be blank (although the value will still be set).
12643      * @param {String} value The value to match
12644      */
12645     setValue : function(v){
12646         if(this.multiple){
12647             this.syncValue();
12648             return;
12649         }
12650         
12651         var text = v;
12652         if(this.valueField){
12653             var r = this.findRecord(this.valueField, v);
12654             if(r){
12655                 text = r.data[this.displayField];
12656             }else if(this.valueNotFoundText !== undefined){
12657                 text = this.valueNotFoundText;
12658             }
12659         }
12660         this.lastSelectionText = text;
12661         if(this.hiddenField){
12662             this.hiddenField.dom.value = v;
12663         }
12664         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12665         this.value = v;
12666         
12667         var close = this.closeTriggerEl();
12668         
12669         if(close){
12670             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12671         }
12672     },
12673     /**
12674      * @property {Object} the last set data for the element
12675      */
12676     
12677     lastData : false,
12678     /**
12679      * Sets the value of the field based on a object which is related to the record format for the store.
12680      * @param {Object} value the value to set as. or false on reset?
12681      */
12682     setFromData : function(o){
12683         
12684         if(this.multiple){
12685             this.addItem(o);
12686             return;
12687         }
12688             
12689         var dv = ''; // display value
12690         var vv = ''; // value value..
12691         this.lastData = o;
12692         if (this.displayField) {
12693             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12694         } else {
12695             // this is an error condition!!!
12696             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12697         }
12698         
12699         if(this.valueField){
12700             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12701         }
12702         
12703         var close = this.closeTriggerEl();
12704         
12705         if(close){
12706             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12707         }
12708         
12709         if(this.hiddenField){
12710             this.hiddenField.dom.value = vv;
12711             
12712             this.lastSelectionText = dv;
12713             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12714             this.value = vv;
12715             return;
12716         }
12717         // no hidden field.. - we store the value in 'value', but still display
12718         // display field!!!!
12719         this.lastSelectionText = dv;
12720         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12721         this.value = vv;
12722         
12723         
12724         
12725     },
12726     // private
12727     reset : function(){
12728         // overridden so that last data is reset..
12729         
12730         if(this.multiple){
12731             this.clearItem();
12732             return;
12733         }
12734         
12735         this.setValue(this.originalValue);
12736         this.clearInvalid();
12737         this.lastData = false;
12738         if (this.view) {
12739             this.view.clearSelections();
12740         }
12741     },
12742     // private
12743     findRecord : function(prop, value){
12744         var record;
12745         if(this.store.getCount() > 0){
12746             this.store.each(function(r){
12747                 if(r.data[prop] == value){
12748                     record = r;
12749                     return false;
12750                 }
12751                 return true;
12752             });
12753         }
12754         return record;
12755     },
12756     
12757     getName: function()
12758     {
12759         // returns hidden if it's set..
12760         if (!this.rendered) {return ''};
12761         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
12762         
12763     },
12764     // private
12765     onViewMove : function(e, t){
12766         this.inKeyMode = false;
12767     },
12768
12769     // private
12770     onViewOver : function(e, t){
12771         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
12772             return;
12773         }
12774         var item = this.view.findItemFromChild(t);
12775         
12776         if(item){
12777             var index = this.view.indexOf(item);
12778             this.select(index, false);
12779         }
12780     },
12781
12782     // private
12783     onViewClick : function(view, doFocus, el, e)
12784     {
12785         var index = this.view.getSelectedIndexes()[0];
12786         
12787         var r = this.store.getAt(index);
12788         
12789         if(this.tickable){
12790             
12791             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
12792                 return;
12793             }
12794             
12795             var rm = false;
12796             var _this = this;
12797             
12798             Roo.each(this.tickItems, function(v,k){
12799                 
12800                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
12801                     Roo.log(v);
12802                     _this.tickItems.splice(k, 1);
12803                     
12804                     if(typeof(e) == 'undefined' && view == false){
12805                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
12806                     }
12807                     
12808                     rm = true;
12809                     return;
12810                 }
12811             });
12812             
12813             if(rm){
12814                 return;
12815             }
12816             
12817             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
12818                 this.tickItems.push(r.data);
12819             }
12820             
12821             if(typeof(e) == 'undefined' && view == false){
12822                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
12823             }
12824                     
12825             return;
12826         }
12827         
12828         if(r){
12829             this.onSelect(r, index);
12830         }
12831         if(doFocus !== false && !this.blockFocus){
12832             this.inputEl().focus();
12833         }
12834     },
12835
12836     // private
12837     restrictHeight : function(){
12838         //this.innerList.dom.style.height = '';
12839         //var inner = this.innerList.dom;
12840         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
12841         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
12842         //this.list.beginUpdate();
12843         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
12844         this.list.alignTo(this.inputEl(), this.listAlign);
12845         this.list.alignTo(this.inputEl(), this.listAlign);
12846         //this.list.endUpdate();
12847     },
12848
12849     // private
12850     onEmptyResults : function(){
12851         
12852         if(this.tickable && this.editable){
12853             this.restrictHeight();
12854             return;
12855         }
12856         
12857         this.collapse();
12858     },
12859
12860     /**
12861      * Returns true if the dropdown list is expanded, else false.
12862      */
12863     isExpanded : function(){
12864         return this.list.isVisible();
12865     },
12866
12867     /**
12868      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
12869      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12870      * @param {String} value The data value of the item to select
12871      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12872      * selected item if it is not currently in view (defaults to true)
12873      * @return {Boolean} True if the value matched an item in the list, else false
12874      */
12875     selectByValue : function(v, scrollIntoView){
12876         if(v !== undefined && v !== null){
12877             var r = this.findRecord(this.valueField || this.displayField, v);
12878             if(r){
12879                 this.select(this.store.indexOf(r), scrollIntoView);
12880                 return true;
12881             }
12882         }
12883         return false;
12884     },
12885
12886     /**
12887      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
12888      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
12889      * @param {Number} index The zero-based index of the list item to select
12890      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
12891      * selected item if it is not currently in view (defaults to true)
12892      */
12893     select : function(index, scrollIntoView){
12894         this.selectedIndex = index;
12895         this.view.select(index);
12896         if(scrollIntoView !== false){
12897             var el = this.view.getNode(index);
12898             /*
12899              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
12900              */
12901             if(el){
12902                 this.list.scrollChildIntoView(el, false);
12903             }
12904         }
12905     },
12906
12907     // private
12908     selectNext : function(){
12909         var ct = this.store.getCount();
12910         if(ct > 0){
12911             if(this.selectedIndex == -1){
12912                 this.select(0);
12913             }else if(this.selectedIndex < ct-1){
12914                 this.select(this.selectedIndex+1);
12915             }
12916         }
12917     },
12918
12919     // private
12920     selectPrev : function(){
12921         var ct = this.store.getCount();
12922         if(ct > 0){
12923             if(this.selectedIndex == -1){
12924                 this.select(0);
12925             }else if(this.selectedIndex != 0){
12926                 this.select(this.selectedIndex-1);
12927             }
12928         }
12929     },
12930
12931     // private
12932     onKeyUp : function(e){
12933         if(this.editable !== false && !e.isSpecialKey()){
12934             this.lastKey = e.getKey();
12935             this.dqTask.delay(this.queryDelay);
12936         }
12937     },
12938
12939     // private
12940     validateBlur : function(){
12941         return !this.list || !this.list.isVisible();   
12942     },
12943
12944     // private
12945     initQuery : function(){
12946         
12947         var v = this.getRawValue();
12948         
12949         if(this.tickable && this.editable){
12950             v = this.tickableInputEl().getValue();
12951         }
12952         
12953         this.doQuery(v);
12954     },
12955
12956     // private
12957     doForce : function(){
12958         if(this.inputEl().dom.value.length > 0){
12959             this.inputEl().dom.value =
12960                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
12961              
12962         }
12963     },
12964
12965     /**
12966      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
12967      * query allowing the query action to be canceled if needed.
12968      * @param {String} query The SQL query to execute
12969      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
12970      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
12971      * saved in the current store (defaults to false)
12972      */
12973     doQuery : function(q, forceAll){
12974         
12975         if(q === undefined || q === null){
12976             q = '';
12977         }
12978         var qe = {
12979             query: q,
12980             forceAll: forceAll,
12981             combo: this,
12982             cancel:false
12983         };
12984         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
12985             return false;
12986         }
12987         q = qe.query;
12988         
12989         forceAll = qe.forceAll;
12990         if(forceAll === true || (q.length >= this.minChars)){
12991             
12992             this.hasQuery = true;
12993             
12994             if(this.lastQuery != q || this.alwaysQuery){
12995                 this.lastQuery = q;
12996                 if(this.mode == 'local'){
12997                     this.selectedIndex = -1;
12998                     if(forceAll){
12999                         this.store.clearFilter();
13000                     }else{
13001                         
13002                         if(this.specialFilter){
13003                             this.fireEvent('specialfilter', this);
13004                             this.onLoad();
13005                             return;
13006                         }
13007                         
13008                         this.store.filter(this.displayField, q);
13009                     }
13010                     
13011                     this.store.fireEvent("datachanged", this.store);
13012                     
13013                     this.onLoad();
13014                     
13015                     
13016                 }else{
13017                     
13018                     this.store.baseParams[this.queryParam] = q;
13019                     
13020                     var options = {params : this.getParams(q)};
13021                     
13022                     if(this.loadNext){
13023                         options.add = true;
13024                         options.params.start = this.page * this.pageSize;
13025                     }
13026                     
13027                     this.store.load(options);
13028                     
13029                     /*
13030                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13031                      *  we should expand the list on onLoad
13032                      *  so command out it
13033                      */
13034 //                    this.expand();
13035                 }
13036             }else{
13037                 this.selectedIndex = -1;
13038                 this.onLoad();   
13039             }
13040         }
13041         
13042         this.loadNext = false;
13043     },
13044     
13045     // private
13046     getParams : function(q){
13047         var p = {};
13048         //p[this.queryParam] = q;
13049         
13050         if(this.pageSize){
13051             p.start = 0;
13052             p.limit = this.pageSize;
13053         }
13054         return p;
13055     },
13056
13057     /**
13058      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13059      */
13060     collapse : function(){
13061         if(!this.isExpanded()){
13062             return;
13063         }
13064         
13065         this.list.hide();
13066         
13067         if(this.tickable){
13068             this.hasFocus = false;
13069             this.okBtn.hide();
13070             this.cancelBtn.hide();
13071             this.trigger.show();
13072             
13073             if(this.editable){
13074                 this.tickableInputEl().dom.value = '';
13075                 this.tickableInputEl().blur();
13076             }
13077             
13078         }
13079         
13080         Roo.get(document).un('mousedown', this.collapseIf, this);
13081         Roo.get(document).un('mousewheel', this.collapseIf, this);
13082         if (!this.editable) {
13083             Roo.get(document).un('keydown', this.listKeyPress, this);
13084         }
13085         this.fireEvent('collapse', this);
13086     },
13087
13088     // private
13089     collapseIf : function(e){
13090         var in_combo  = e.within(this.el);
13091         var in_list =  e.within(this.list);
13092         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13093         
13094         if (in_combo || in_list || is_list) {
13095             //e.stopPropagation();
13096             return;
13097         }
13098         
13099         if(this.tickable){
13100             this.onTickableFooterButtonClick(e, false, false);
13101         }
13102
13103         this.collapse();
13104         
13105     },
13106
13107     /**
13108      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13109      */
13110     expand : function(){
13111        
13112         if(this.isExpanded() || !this.hasFocus){
13113             return;
13114         }
13115         
13116         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13117         this.list.setWidth(lw);
13118         
13119         
13120          Roo.log('expand');
13121         
13122         this.list.show();
13123         
13124         this.restrictHeight();
13125         
13126         if(this.tickable){
13127             
13128             this.tickItems = Roo.apply([], this.item);
13129             
13130             this.okBtn.show();
13131             this.cancelBtn.show();
13132             this.trigger.hide();
13133             
13134             if(this.editable){
13135                 this.tickableInputEl().focus();
13136             }
13137             
13138         }
13139         
13140         Roo.get(document).on('mousedown', this.collapseIf, this);
13141         Roo.get(document).on('mousewheel', this.collapseIf, this);
13142         if (!this.editable) {
13143             Roo.get(document).on('keydown', this.listKeyPress, this);
13144         }
13145         
13146         this.fireEvent('expand', this);
13147     },
13148
13149     // private
13150     // Implements the default empty TriggerField.onTriggerClick function
13151     onTriggerClick : function(e)
13152     {
13153         Roo.log('trigger click');
13154         
13155         if(this.disabled || !this.triggerList){
13156             return;
13157         }
13158         
13159         this.page = 0;
13160         this.loadNext = false;
13161         
13162         if(this.isExpanded()){
13163             this.collapse();
13164             if (!this.blockFocus) {
13165                 this.inputEl().focus();
13166             }
13167             
13168         }else {
13169             this.hasFocus = true;
13170             if(this.triggerAction == 'all') {
13171                 this.doQuery(this.allQuery, true);
13172             } else {
13173                 this.doQuery(this.getRawValue());
13174             }
13175             if (!this.blockFocus) {
13176                 this.inputEl().focus();
13177             }
13178         }
13179     },
13180     
13181     onTickableTriggerClick : function(e)
13182     {
13183         if(this.disabled){
13184             return;
13185         }
13186         
13187         this.page = 0;
13188         this.loadNext = false;
13189         this.hasFocus = true;
13190         
13191         if(this.triggerAction == 'all') {
13192             this.doQuery(this.allQuery, true);
13193         } else {
13194             this.doQuery(this.getRawValue());
13195         }
13196     },
13197     
13198     onSearchFieldClick : function(e)
13199     {
13200         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13201             this.onTickableFooterButtonClick(e, false, false);
13202             return;
13203         }
13204         
13205         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13206             return;
13207         }
13208         
13209         this.page = 0;
13210         this.loadNext = false;
13211         this.hasFocus = true;
13212         
13213         if(this.triggerAction == 'all') {
13214             this.doQuery(this.allQuery, true);
13215         } else {
13216             this.doQuery(this.getRawValue());
13217         }
13218     },
13219     
13220     listKeyPress : function(e)
13221     {
13222         //Roo.log('listkeypress');
13223         // scroll to first matching element based on key pres..
13224         if (e.isSpecialKey()) {
13225             return false;
13226         }
13227         var k = String.fromCharCode(e.getKey()).toUpperCase();
13228         //Roo.log(k);
13229         var match  = false;
13230         var csel = this.view.getSelectedNodes();
13231         var cselitem = false;
13232         if (csel.length) {
13233             var ix = this.view.indexOf(csel[0]);
13234             cselitem  = this.store.getAt(ix);
13235             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13236                 cselitem = false;
13237             }
13238             
13239         }
13240         
13241         this.store.each(function(v) { 
13242             if (cselitem) {
13243                 // start at existing selection.
13244                 if (cselitem.id == v.id) {
13245                     cselitem = false;
13246                 }
13247                 return true;
13248             }
13249                 
13250             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13251                 match = this.store.indexOf(v);
13252                 return false;
13253             }
13254             return true;
13255         }, this);
13256         
13257         if (match === false) {
13258             return true; // no more action?
13259         }
13260         // scroll to?
13261         this.view.select(match);
13262         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13263         sn.scrollIntoView(sn.dom.parentNode, false);
13264     },
13265     
13266     onViewScroll : function(e, t){
13267         
13268         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
13269             return;
13270         }
13271         
13272         this.hasQuery = true;
13273         
13274         this.loading = this.list.select('.loading', true).first();
13275         
13276         if(this.loading === null){
13277             this.list.createChild({
13278                 tag: 'div',
13279                 cls: 'loading select2-more-results select2-active',
13280                 html: 'Loading more results...'
13281             });
13282             
13283             this.loading = this.list.select('.loading', true).first();
13284             
13285             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13286             
13287             this.loading.hide();
13288         }
13289         
13290         this.loading.show();
13291         
13292         var _combo = this;
13293         
13294         this.page++;
13295         this.loadNext = true;
13296         
13297         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13298         
13299         return;
13300     },
13301     
13302     addItem : function(o)
13303     {   
13304         var dv = ''; // display value
13305         
13306         if (this.displayField) {
13307             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13308         } else {
13309             // this is an error condition!!!
13310             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13311         }
13312         
13313         if(!dv.length){
13314             return;
13315         }
13316         
13317         var choice = this.choices.createChild({
13318             tag: 'li',
13319             cls: 'select2-search-choice',
13320             cn: [
13321                 {
13322                     tag: 'div',
13323                     html: dv
13324                 },
13325                 {
13326                     tag: 'a',
13327                     href: '#',
13328                     cls: 'select2-search-choice-close',
13329                     tabindex: '-1'
13330                 }
13331             ]
13332             
13333         }, this.searchField);
13334         
13335         var close = choice.select('a.select2-search-choice-close', true).first();
13336         
13337         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13338         
13339         this.item.push(o);
13340         
13341         this.lastData = o;
13342         
13343         this.syncValue();
13344         
13345         this.inputEl().dom.value = '';
13346         
13347         this.validate();
13348     },
13349     
13350     onRemoveItem : function(e, _self, o)
13351     {
13352         e.preventDefault();
13353         
13354         this.lastItem = Roo.apply([], this.item);
13355         
13356         var index = this.item.indexOf(o.data) * 1;
13357         
13358         if( index < 0){
13359             Roo.log('not this item?!');
13360             return;
13361         }
13362         
13363         this.item.splice(index, 1);
13364         o.item.remove();
13365         
13366         this.syncValue();
13367         
13368         this.fireEvent('remove', this, e);
13369         
13370         this.validate();
13371         
13372     },
13373     
13374     syncValue : function()
13375     {
13376         if(!this.item.length){
13377             this.clearValue();
13378             return;
13379         }
13380             
13381         var value = [];
13382         var _this = this;
13383         Roo.each(this.item, function(i){
13384             if(_this.valueField){
13385                 value.push(i[_this.valueField]);
13386                 return;
13387             }
13388
13389             value.push(i);
13390         });
13391
13392         this.value = value.join(',');
13393
13394         if(this.hiddenField){
13395             this.hiddenField.dom.value = this.value;
13396         }
13397         
13398         this.store.fireEvent("datachanged", this.store);
13399     },
13400     
13401     clearItem : function()
13402     {
13403         if(!this.multiple){
13404             return;
13405         }
13406         
13407         this.item = [];
13408         
13409         Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
13410            c.remove();
13411         });
13412         
13413         this.syncValue();
13414         
13415         this.validate();
13416         
13417         if(this.tickable && !Roo.isTouch){
13418             this.view.refresh();
13419         }
13420     },
13421     
13422     inputEl: function ()
13423     {
13424         if(Roo.isTouch && this.mobileTouchView){
13425             return this.el.select('input.form-control',true).first();
13426         }
13427         
13428         if(this.tickable){
13429             return this.searchField;
13430         }
13431         
13432         return this.el.select('input.form-control',true).first();
13433     },
13434     
13435     
13436     onTickableFooterButtonClick : function(e, btn, el)
13437     {
13438         e.preventDefault();
13439         
13440         this.lastItem = Roo.apply([], this.item);
13441         
13442         if(btn && btn.name == 'cancel'){
13443             this.tickItems = Roo.apply([], this.item);
13444             this.collapse();
13445             return;
13446         }
13447         
13448         this.clearItem();
13449         
13450         var _this = this;
13451         
13452         Roo.each(this.tickItems, function(o){
13453             _this.addItem(o);
13454         });
13455         
13456         this.collapse();
13457         
13458     },
13459     
13460     validate : function()
13461     {
13462         var v = this.getRawValue();
13463         
13464         if(this.multiple){
13465             v = this.getValue();
13466         }
13467         
13468         if(this.disabled || this.allowBlank || v.length){
13469             this.markValid();
13470             return true;
13471         }
13472         
13473         this.markInvalid();
13474         return false;
13475     },
13476     
13477     tickableInputEl : function()
13478     {
13479         if(!this.tickable || !this.editable){
13480             return this.inputEl();
13481         }
13482         
13483         return this.inputEl().select('.select2-search-field-input', true).first();
13484     },
13485     
13486     
13487     getAutoCreateTouchView : function()
13488     {
13489         var id = Roo.id();
13490         
13491         var cfg = {
13492             cls: 'form-group' //input-group
13493         };
13494         
13495         var input =  {
13496             tag: 'input',
13497             id : id,
13498             type : this.inputType,
13499             cls : 'form-control x-combo-noedit',
13500             autocomplete: 'new-password',
13501             placeholder : this.placeholder || '',
13502             readonly : true
13503         };
13504         
13505         if (this.name) {
13506             input.name = this.name;
13507         }
13508         
13509         if (this.size) {
13510             input.cls += ' input-' + this.size;
13511         }
13512         
13513         if (this.disabled) {
13514             input.disabled = true;
13515         }
13516         
13517         var inputblock = {
13518             cls : '',
13519             cn : [
13520                 input
13521             ]
13522         };
13523         
13524         if(this.before){
13525             inputblock.cls += ' input-group';
13526             
13527             inputblock.cn.unshift({
13528                 tag :'span',
13529                 cls : 'input-group-addon',
13530                 html : this.before
13531             });
13532         }
13533         
13534         if(this.removable && !this.multiple){
13535             inputblock.cls += ' roo-removable';
13536             
13537             inputblock.cn.push({
13538                 tag: 'button',
13539                 html : 'x',
13540                 cls : 'roo-combo-removable-btn close'
13541             });
13542         }
13543
13544         if(this.hasFeedback && !this.allowBlank){
13545             
13546             inputblock.cls += ' has-feedback';
13547             
13548             inputblock.cn.push({
13549                 tag: 'span',
13550                 cls: 'glyphicon form-control-feedback'
13551             });
13552             
13553         }
13554         
13555         if (this.after) {
13556             
13557             inputblock.cls += (this.before) ? '' : ' input-group';
13558             
13559             inputblock.cn.push({
13560                 tag :'span',
13561                 cls : 'input-group-addon',
13562                 html : this.after
13563             });
13564         }
13565
13566         var box = {
13567             tag: 'div',
13568             cn: [
13569                 {
13570                     tag: 'input',
13571                     type : 'hidden',
13572                     cls: 'form-hidden-field'
13573                 },
13574                 inputblock
13575             ]
13576             
13577         };
13578         
13579         if(this.multiple){
13580             box = {
13581                 tag: 'div',
13582                 cn: [
13583                     {
13584                         tag: 'input',
13585                         type : 'hidden',
13586                         cls: 'form-hidden-field'
13587                     },
13588                     {
13589                         tag: 'ul',
13590                         cls: 'select2-choices',
13591                         cn:[
13592                             {
13593                                 tag: 'li',
13594                                 cls: 'select2-search-field',
13595                                 cn: [
13596
13597                                     inputblock
13598                                 ]
13599                             }
13600                         ]
13601                     }
13602                 ]
13603             }
13604         };
13605         
13606         var combobox = {
13607             cls: 'select2-container input-group',
13608             cn: [
13609                 box
13610             ]
13611         };
13612         
13613         if(this.multiple){
13614             combobox.cls += ' select2-container-multi';
13615         }
13616         
13617         var align = this.labelAlign || this.parentLabelAlign();
13618         
13619         cfg.cn = combobox;
13620         
13621         if(this.fieldLabel.length){
13622             
13623             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13624             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13625             
13626             cfg.cn = [
13627                 {
13628                     tag: 'label',
13629                     cls : 'control-label ' + lw,
13630                     html : this.fieldLabel
13631
13632                 },
13633                 {
13634                     cls : cw, 
13635                     cn: [
13636                         combobox
13637                     ]
13638                 }
13639             ];
13640         }
13641         
13642         var settings = this;
13643         
13644         ['xs','sm','md','lg'].map(function(size){
13645             if (settings[size]) {
13646                 cfg.cls += ' col-' + size + '-' + settings[size];
13647             }
13648         });
13649         
13650         return cfg;
13651     },
13652     
13653     initTouchView : function()
13654     {
13655         this.renderTouchView();
13656         
13657         this.touchViewEl.on('scroll', function(){
13658             this.el.dom.scrollTop = 0;
13659         }, this);
13660         
13661         this.originalValue = this.getValue();
13662         
13663         this.inputEl().on("click", this.showTouchView, this);
13664         
13665         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13666         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13667         
13668         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13669         
13670         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13671         this.store.on('load', this.onTouchViewLoad, this);
13672         this.store.on('loadexception', this.onTouchViewLoadException, this);
13673         
13674         if(this.hiddenName){
13675             
13676             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13677             
13678             this.hiddenField.dom.value =
13679                 this.hiddenValue !== undefined ? this.hiddenValue :
13680                 this.value !== undefined ? this.value : '';
13681         
13682             this.el.dom.removeAttribute('name');
13683             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13684         }
13685         
13686         if(this.multiple){
13687             this.choices = this.el.select('ul.select2-choices', true).first();
13688             this.searchField = this.el.select('ul li.select2-search-field', true).first();
13689         }
13690         
13691         if(this.removable && !this.multiple){
13692             var close = this.closeTriggerEl();
13693             if(close){
13694                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13695                 close.on('click', this.removeBtnClick, this, close);
13696             }
13697         }
13698         /*
13699          * fix the bug in Safari iOS8
13700          */
13701         this.inputEl().on("focus", function(e){
13702             document.activeElement.blur();
13703         }, this);
13704         
13705         return;
13706         
13707         
13708     },
13709     
13710     renderTouchView : function()
13711     {
13712         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
13713         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13714         
13715         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
13716         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13717         
13718         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
13719         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13720         this.touchViewBodyEl.setStyle('overflow', 'auto');
13721         
13722         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
13723         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13724         
13725         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
13726         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13727         
13728     },
13729     
13730     showTouchView : function()
13731     {
13732         if(this.disabled){
13733             return;
13734         }
13735         
13736         this.touchViewHeaderEl.hide();
13737
13738         if(this.fieldLabel.length){
13739             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
13740             this.touchViewHeaderEl.show();
13741         }
13742
13743         this.touchViewEl.show();
13744
13745         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
13746         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13747
13748         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13749
13750         if(this.fieldLabel.length){
13751             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13752         }
13753         
13754         this.touchViewBodyEl.setHeight(bodyHeight);
13755
13756         if(this.animate){
13757             var _this = this;
13758             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
13759         }else{
13760             this.touchViewEl.addClass('in');
13761         }
13762
13763         this.doTouchViewQuery();
13764         
13765     },
13766     
13767     hideTouchView : function()
13768     {
13769         this.touchViewEl.removeClass('in');
13770
13771         if(this.animate){
13772             var _this = this;
13773             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
13774         }else{
13775             this.touchViewEl.setStyle('display', 'none');
13776         }
13777         
13778     },
13779     
13780     setTouchViewValue : function()
13781     {
13782         if(this.multiple){
13783             this.clearItem();
13784         
13785             var _this = this;
13786
13787             Roo.each(this.tickItems, function(o){
13788                 this.addItem(o);
13789             }, this);
13790         }
13791         
13792         this.hideTouchView();
13793     },
13794     
13795     doTouchViewQuery : function()
13796     {
13797         var qe = {
13798             query: '',
13799             forceAll: true,
13800             combo: this,
13801             cancel:false
13802         };
13803         
13804         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
13805             return false;
13806         }
13807         
13808         if(!this.alwaysQuery || this.mode == 'local'){
13809             this.onTouchViewLoad();
13810             return;
13811         }
13812         
13813         this.store.load();
13814     },
13815     
13816     onTouchViewBeforeLoad : function(combo,opts)
13817     {
13818         return;
13819     },
13820
13821     // private
13822     onTouchViewLoad : function()
13823     {
13824         if(this.store.getCount() < 1){
13825             this.onTouchViewEmptyResults();
13826             return;
13827         }
13828         
13829         this.clearTouchView();
13830         
13831         var rawValue = this.getRawValue();
13832         
13833         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
13834         
13835         this.tickItems = [];
13836         
13837         this.store.data.each(function(d, rowIndex){
13838             var row = this.touchViewListGroup.createChild(template);
13839             
13840             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
13841                 var cfg = {
13842                     data : d.data,
13843                     html : d.data[this.displayField]
13844                 };
13845                 
13846                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
13847                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
13848                 }
13849             }
13850             
13851             if(!this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue()){
13852                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13853             }
13854             
13855             if(this.multiple && this.valueField && typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1){
13856                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13857                 this.tickItems.push(d.data);
13858             }
13859             
13860             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
13861             
13862         }, this);
13863         
13864         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
13865         
13866         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
13867
13868         if(this.fieldLabel.length){
13869             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
13870         }
13871
13872         var listHeight = this.touchViewListGroup.getHeight();
13873         
13874         var _this = this;
13875         
13876         if(firstChecked && listHeight > bodyHeight){
13877             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
13878         }
13879         
13880     },
13881     
13882     onTouchViewLoadException : function()
13883     {
13884         this.hideTouchView();
13885     },
13886     
13887     onTouchViewEmptyResults : function()
13888     {
13889         this.clearTouchView();
13890         
13891         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
13892         
13893         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
13894         
13895     },
13896     
13897     clearTouchView : function()
13898     {
13899         this.touchViewListGroup.dom.innerHTML = '';
13900     },
13901     
13902     onTouchViewClick : function(e, el, o)
13903     {
13904         e.preventDefault();
13905         
13906         var row = o.row;
13907         var rowIndex = o.rowIndex;
13908         
13909         var r = this.store.getAt(rowIndex);
13910         
13911         if(!this.multiple){
13912             Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
13913                 c.dom.removeAttribute('checked');
13914             }, this);
13915             
13916             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13917         
13918             this.setFromData(r.data);
13919             
13920             var close = this.closeTriggerEl();
13921         
13922             if(close){
13923                 close.show();
13924             }
13925
13926             this.hideTouchView();
13927             
13928             this.fireEvent('select', this, r, rowIndex);
13929             
13930             return;
13931         }
13932         
13933         if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
13934             row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
13935             this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
13936             return;
13937         }
13938         
13939         row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
13940         this.addItem(r.data);
13941         this.tickItems.push(r.data);
13942         
13943     }
13944     
13945
13946     /** 
13947     * @cfg {Boolean} grow 
13948     * @hide 
13949     */
13950     /** 
13951     * @cfg {Number} growMin 
13952     * @hide 
13953     */
13954     /** 
13955     * @cfg {Number} growMax 
13956     * @hide 
13957     */
13958     /**
13959      * @hide
13960      * @method autoSize
13961      */
13962 });
13963
13964 Roo.apply(Roo.bootstrap.ComboBox,  {
13965     
13966     header : {
13967         tag: 'div',
13968         cls: 'modal-header',
13969         cn: [
13970             {
13971                 tag: 'h4',
13972                 cls: 'modal-title'
13973             }
13974         ]
13975     },
13976     
13977     body : {
13978         tag: 'div',
13979         cls: 'modal-body',
13980         cn: [
13981             {
13982                 tag: 'ul',
13983                 cls: 'list-group'
13984             }
13985         ]
13986     },
13987     
13988     listItemRadio : {
13989         tag: 'li',
13990         cls: 'list-group-item',
13991         cn: [
13992             {
13993                 tag: 'span',
13994                 cls: 'roo-combobox-list-group-item-value'
13995             },
13996             {
13997                 tag: 'div',
13998                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
13999                 cn: [
14000                     {
14001                         tag: 'input',
14002                         type: 'radio'
14003                     },
14004                     {
14005                         tag: 'label'
14006                     }
14007                 ]
14008             }
14009         ]
14010     },
14011     
14012     listItemCheckbox : {
14013         tag: 'li',
14014         cls: 'list-group-item',
14015         cn: [
14016             {
14017                 tag: 'span',
14018                 cls: 'roo-combobox-list-group-item-value'
14019             },
14020             {
14021                 tag: 'div',
14022                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14023                 cn: [
14024                     {
14025                         tag: 'input',
14026                         type: 'checkbox'
14027                     },
14028                     {
14029                         tag: 'label'
14030                     }
14031                 ]
14032             }
14033         ]
14034     },
14035     
14036     emptyResult : {
14037         tag: 'div',
14038         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14039     },
14040     
14041     footer : {
14042         tag: 'div',
14043         cls: 'modal-footer',
14044         cn: [
14045             {
14046                 tag: 'div',
14047                 cls: 'row',
14048                 cn: [
14049                     {
14050                         tag: 'div',
14051                         cls: 'col-xs-6 text-left',
14052                         cn: {
14053                             tag: 'button',
14054                             cls: 'btn btn-danger roo-touch-view-cancel',
14055                             html: 'Cancel'
14056                         }
14057                     },
14058                     {
14059                         tag: 'div',
14060                         cls: 'col-xs-6 text-right',
14061                         cn: {
14062                             tag: 'button',
14063                             cls: 'btn btn-success roo-touch-view-ok',
14064                             html: 'OK'
14065                         }
14066                     }
14067                 ]
14068             }
14069         ]
14070         
14071     }
14072 });
14073
14074 Roo.apply(Roo.bootstrap.ComboBox,  {
14075     
14076     touchViewTemplate : {
14077         tag: 'div',
14078         cls: 'modal fade roo-combobox-touch-view',
14079         cn: [
14080             {
14081                 tag: 'div',
14082                 cls: 'modal-dialog',
14083                 style : 'position:fixed', // we have to fix position....
14084                 cn: [
14085                     {
14086                         tag: 'div',
14087                         cls: 'modal-content',
14088                         cn: [
14089                             Roo.bootstrap.ComboBox.header,
14090                             Roo.bootstrap.ComboBox.body,
14091                             Roo.bootstrap.ComboBox.footer
14092                         ]
14093                     }
14094                 ]
14095             }
14096         ]
14097     }
14098 });/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.View
14111  * @extends Roo.util.Observable
14112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14113  * This class also supports single and multi selection modes. <br>
14114  * Create a data model bound view:
14115  <pre><code>
14116  var store = new Roo.data.Store(...);
14117
14118  var view = new Roo.View({
14119     el : "my-element",
14120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14121  
14122     singleSelect: true,
14123     selectedClass: "ydataview-selected",
14124     store: store
14125  });
14126
14127  // listen for node click?
14128  view.on("click", function(vw, index, node, e){
14129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14130  });
14131
14132  // load XML data
14133  dataModel.load("foobar.xml");
14134  </code></pre>
14135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14136  * <br><br>
14137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14139  * 
14140  * Note: old style constructor is still suported (container, template, config)
14141  * 
14142  * @constructor
14143  * Create a new View
14144  * @param {Object} config The config object
14145  * 
14146  */
14147 Roo.View = function(config, depreciated_tpl, depreciated_config){
14148     
14149     this.parent = false;
14150     
14151     if (typeof(depreciated_tpl) == 'undefined') {
14152         // new way.. - universal constructor.
14153         Roo.apply(this, config);
14154         this.el  = Roo.get(this.el);
14155     } else {
14156         // old format..
14157         this.el  = Roo.get(config);
14158         this.tpl = depreciated_tpl;
14159         Roo.apply(this, depreciated_config);
14160     }
14161     this.wrapEl  = this.el.wrap().wrap();
14162     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14163     
14164     
14165     if(typeof(this.tpl) == "string"){
14166         this.tpl = new Roo.Template(this.tpl);
14167     } else {
14168         // support xtype ctors..
14169         this.tpl = new Roo.factory(this.tpl, Roo);
14170     }
14171     
14172     
14173     this.tpl.compile();
14174     
14175     /** @private */
14176     this.addEvents({
14177         /**
14178          * @event beforeclick
14179          * Fires before a click is processed. Returns false to cancel the default action.
14180          * @param {Roo.View} this
14181          * @param {Number} index The index of the target node
14182          * @param {HTMLElement} node The target node
14183          * @param {Roo.EventObject} e The raw event object
14184          */
14185             "beforeclick" : true,
14186         /**
14187          * @event click
14188          * Fires when a template node is clicked.
14189          * @param {Roo.View} this
14190          * @param {Number} index The index of the target node
14191          * @param {HTMLElement} node The target node
14192          * @param {Roo.EventObject} e The raw event object
14193          */
14194             "click" : true,
14195         /**
14196          * @event dblclick
14197          * Fires when a template node is double clicked.
14198          * @param {Roo.View} this
14199          * @param {Number} index The index of the target node
14200          * @param {HTMLElement} node The target node
14201          * @param {Roo.EventObject} e The raw event object
14202          */
14203             "dblclick" : true,
14204         /**
14205          * @event contextmenu
14206          * Fires when a template node is right clicked.
14207          * @param {Roo.View} this
14208          * @param {Number} index The index of the target node
14209          * @param {HTMLElement} node The target node
14210          * @param {Roo.EventObject} e The raw event object
14211          */
14212             "contextmenu" : true,
14213         /**
14214          * @event selectionchange
14215          * Fires when the selected nodes change.
14216          * @param {Roo.View} this
14217          * @param {Array} selections Array of the selected nodes
14218          */
14219             "selectionchange" : true,
14220     
14221         /**
14222          * @event beforeselect
14223          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14224          * @param {Roo.View} this
14225          * @param {HTMLElement} node The node to be selected
14226          * @param {Array} selections Array of currently selected nodes
14227          */
14228             "beforeselect" : true,
14229         /**
14230          * @event preparedata
14231          * Fires on every row to render, to allow you to change the data.
14232          * @param {Roo.View} this
14233          * @param {Object} data to be rendered (change this)
14234          */
14235           "preparedata" : true
14236           
14237           
14238         });
14239
14240
14241
14242     this.el.on({
14243         "click": this.onClick,
14244         "dblclick": this.onDblClick,
14245         "contextmenu": this.onContextMenu,
14246         scope:this
14247     });
14248
14249     this.selections = [];
14250     this.nodes = [];
14251     this.cmp = new Roo.CompositeElementLite([]);
14252     if(this.store){
14253         this.store = Roo.factory(this.store, Roo.data);
14254         this.setStore(this.store, true);
14255     }
14256     
14257     if ( this.footer && this.footer.xtype) {
14258            
14259          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14260         
14261         this.footer.dataSource = this.store;
14262         this.footer.container = fctr;
14263         this.footer = Roo.factory(this.footer, Roo);
14264         fctr.insertFirst(this.el);
14265         
14266         // this is a bit insane - as the paging toolbar seems to detach the el..
14267 //        dom.parentNode.parentNode.parentNode
14268          // they get detached?
14269     }
14270     
14271     
14272     Roo.View.superclass.constructor.call(this);
14273     
14274     
14275 };
14276
14277 Roo.extend(Roo.View, Roo.util.Observable, {
14278     
14279      /**
14280      * @cfg {Roo.data.Store} store Data store to load data from.
14281      */
14282     store : false,
14283     
14284     /**
14285      * @cfg {String|Roo.Element} el The container element.
14286      */
14287     el : '',
14288     
14289     /**
14290      * @cfg {String|Roo.Template} tpl The template used by this View 
14291      */
14292     tpl : false,
14293     /**
14294      * @cfg {String} dataName the named area of the template to use as the data area
14295      *                          Works with domtemplates roo-name="name"
14296      */
14297     dataName: false,
14298     /**
14299      * @cfg {String} selectedClass The css class to add to selected nodes
14300      */
14301     selectedClass : "x-view-selected",
14302      /**
14303      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14304      */
14305     emptyText : "",
14306     
14307     /**
14308      * @cfg {String} text to display on mask (default Loading)
14309      */
14310     mask : false,
14311     /**
14312      * @cfg {Boolean} multiSelect Allow multiple selection
14313      */
14314     multiSelect : false,
14315     /**
14316      * @cfg {Boolean} singleSelect Allow single selection
14317      */
14318     singleSelect:  false,
14319     
14320     /**
14321      * @cfg {Boolean} toggleSelect - selecting 
14322      */
14323     toggleSelect : false,
14324     
14325     /**
14326      * @cfg {Boolean} tickable - selecting 
14327      */
14328     tickable : false,
14329     
14330     /**
14331      * Returns the element this view is bound to.
14332      * @return {Roo.Element}
14333      */
14334     getEl : function(){
14335         return this.wrapEl;
14336     },
14337     
14338     
14339
14340     /**
14341      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14342      */
14343     refresh : function(){
14344         //Roo.log('refresh');
14345         var t = this.tpl;
14346         
14347         // if we are using something like 'domtemplate', then
14348         // the what gets used is:
14349         // t.applySubtemplate(NAME, data, wrapping data..)
14350         // the outer template then get' applied with
14351         //     the store 'extra data'
14352         // and the body get's added to the
14353         //      roo-name="data" node?
14354         //      <span class='roo-tpl-{name}'></span> ?????
14355         
14356         
14357         
14358         this.clearSelections();
14359         this.el.update("");
14360         var html = [];
14361         var records = this.store.getRange();
14362         if(records.length < 1) {
14363             
14364             // is this valid??  = should it render a template??
14365             
14366             this.el.update(this.emptyText);
14367             return;
14368         }
14369         var el = this.el;
14370         if (this.dataName) {
14371             this.el.update(t.apply(this.store.meta)); //????
14372             el = this.el.child('.roo-tpl-' + this.dataName);
14373         }
14374         
14375         for(var i = 0, len = records.length; i < len; i++){
14376             var data = this.prepareData(records[i].data, i, records[i]);
14377             this.fireEvent("preparedata", this, data, i, records[i]);
14378             
14379             var d = Roo.apply({}, data);
14380             
14381             if(this.tickable){
14382                 Roo.apply(d, {'roo-id' : Roo.id()});
14383                 
14384                 var _this = this;
14385             
14386                 Roo.each(this.parent.item, function(item){
14387                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14388                         return;
14389                     }
14390                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14391                 });
14392             }
14393             
14394             html[html.length] = Roo.util.Format.trim(
14395                 this.dataName ?
14396                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14397                     t.apply(d)
14398             );
14399         }
14400         
14401         
14402         
14403         el.update(html.join(""));
14404         this.nodes = el.dom.childNodes;
14405         this.updateIndexes(0);
14406     },
14407     
14408
14409     /**
14410      * Function to override to reformat the data that is sent to
14411      * the template for each node.
14412      * DEPRICATED - use the preparedata event handler.
14413      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14414      * a JSON object for an UpdateManager bound view).
14415      */
14416     prepareData : function(data, index, record)
14417     {
14418         this.fireEvent("preparedata", this, data, index, record);
14419         return data;
14420     },
14421
14422     onUpdate : function(ds, record){
14423         // Roo.log('on update');   
14424         this.clearSelections();
14425         var index = this.store.indexOf(record);
14426         var n = this.nodes[index];
14427         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14428         n.parentNode.removeChild(n);
14429         this.updateIndexes(index, index);
14430     },
14431
14432     
14433     
14434 // --------- FIXME     
14435     onAdd : function(ds, records, index)
14436     {
14437         //Roo.log(['on Add', ds, records, index] );        
14438         this.clearSelections();
14439         if(this.nodes.length == 0){
14440             this.refresh();
14441             return;
14442         }
14443         var n = this.nodes[index];
14444         for(var i = 0, len = records.length; i < len; i++){
14445             var d = this.prepareData(records[i].data, i, records[i]);
14446             if(n){
14447                 this.tpl.insertBefore(n, d);
14448             }else{
14449                 
14450                 this.tpl.append(this.el, d);
14451             }
14452         }
14453         this.updateIndexes(index);
14454     },
14455
14456     onRemove : function(ds, record, index){
14457        // Roo.log('onRemove');
14458         this.clearSelections();
14459         var el = this.dataName  ?
14460             this.el.child('.roo-tpl-' + this.dataName) :
14461             this.el; 
14462         
14463         el.dom.removeChild(this.nodes[index]);
14464         this.updateIndexes(index);
14465     },
14466
14467     /**
14468      * Refresh an individual node.
14469      * @param {Number} index
14470      */
14471     refreshNode : function(index){
14472         this.onUpdate(this.store, this.store.getAt(index));
14473     },
14474
14475     updateIndexes : function(startIndex, endIndex){
14476         var ns = this.nodes;
14477         startIndex = startIndex || 0;
14478         endIndex = endIndex || ns.length - 1;
14479         for(var i = startIndex; i <= endIndex; i++){
14480             ns[i].nodeIndex = i;
14481         }
14482     },
14483
14484     /**
14485      * Changes the data store this view uses and refresh the view.
14486      * @param {Store} store
14487      */
14488     setStore : function(store, initial){
14489         if(!initial && this.store){
14490             this.store.un("datachanged", this.refresh);
14491             this.store.un("add", this.onAdd);
14492             this.store.un("remove", this.onRemove);
14493             this.store.un("update", this.onUpdate);
14494             this.store.un("clear", this.refresh);
14495             this.store.un("beforeload", this.onBeforeLoad);
14496             this.store.un("load", this.onLoad);
14497             this.store.un("loadexception", this.onLoad);
14498         }
14499         if(store){
14500           
14501             store.on("datachanged", this.refresh, this);
14502             store.on("add", this.onAdd, this);
14503             store.on("remove", this.onRemove, this);
14504             store.on("update", this.onUpdate, this);
14505             store.on("clear", this.refresh, this);
14506             store.on("beforeload", this.onBeforeLoad, this);
14507             store.on("load", this.onLoad, this);
14508             store.on("loadexception", this.onLoad, this);
14509         }
14510         
14511         if(store){
14512             this.refresh();
14513         }
14514     },
14515     /**
14516      * onbeforeLoad - masks the loading area.
14517      *
14518      */
14519     onBeforeLoad : function(store,opts)
14520     {
14521          //Roo.log('onBeforeLoad');   
14522         if (!opts.add) {
14523             this.el.update("");
14524         }
14525         this.el.mask(this.mask ? this.mask : "Loading" ); 
14526     },
14527     onLoad : function ()
14528     {
14529         this.el.unmask();
14530     },
14531     
14532
14533     /**
14534      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14535      * @param {HTMLElement} node
14536      * @return {HTMLElement} The template node
14537      */
14538     findItemFromChild : function(node){
14539         var el = this.dataName  ?
14540             this.el.child('.roo-tpl-' + this.dataName,true) :
14541             this.el.dom; 
14542         
14543         if(!node || node.parentNode == el){
14544                     return node;
14545             }
14546             var p = node.parentNode;
14547             while(p && p != el){
14548             if(p.parentNode == el){
14549                 return p;
14550             }
14551             p = p.parentNode;
14552         }
14553             return null;
14554     },
14555
14556     /** @ignore */
14557     onClick : function(e){
14558         var item = this.findItemFromChild(e.getTarget());
14559         if(item){
14560             var index = this.indexOf(item);
14561             if(this.onItemClick(item, index, e) !== false){
14562                 this.fireEvent("click", this, index, item, e);
14563             }
14564         }else{
14565             this.clearSelections();
14566         }
14567     },
14568
14569     /** @ignore */
14570     onContextMenu : function(e){
14571         var item = this.findItemFromChild(e.getTarget());
14572         if(item){
14573             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14574         }
14575     },
14576
14577     /** @ignore */
14578     onDblClick : function(e){
14579         var item = this.findItemFromChild(e.getTarget());
14580         if(item){
14581             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14582         }
14583     },
14584
14585     onItemClick : function(item, index, e)
14586     {
14587         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14588             return false;
14589         }
14590         if (this.toggleSelect) {
14591             var m = this.isSelected(item) ? 'unselect' : 'select';
14592             //Roo.log(m);
14593             var _t = this;
14594             _t[m](item, true, false);
14595             return true;
14596         }
14597         if(this.multiSelect || this.singleSelect){
14598             if(this.multiSelect && e.shiftKey && this.lastSelection){
14599                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14600             }else{
14601                 this.select(item, this.multiSelect && e.ctrlKey);
14602                 this.lastSelection = item;
14603             }
14604             
14605             if(!this.tickable){
14606                 e.preventDefault();
14607             }
14608             
14609         }
14610         return true;
14611     },
14612
14613     /**
14614      * Get the number of selected nodes.
14615      * @return {Number}
14616      */
14617     getSelectionCount : function(){
14618         return this.selections.length;
14619     },
14620
14621     /**
14622      * Get the currently selected nodes.
14623      * @return {Array} An array of HTMLElements
14624      */
14625     getSelectedNodes : function(){
14626         return this.selections;
14627     },
14628
14629     /**
14630      * Get the indexes of the selected nodes.
14631      * @return {Array}
14632      */
14633     getSelectedIndexes : function(){
14634         var indexes = [], s = this.selections;
14635         for(var i = 0, len = s.length; i < len; i++){
14636             indexes.push(s[i].nodeIndex);
14637         }
14638         return indexes;
14639     },
14640
14641     /**
14642      * Clear all selections
14643      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14644      */
14645     clearSelections : function(suppressEvent){
14646         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14647             this.cmp.elements = this.selections;
14648             this.cmp.removeClass(this.selectedClass);
14649             this.selections = [];
14650             if(!suppressEvent){
14651                 this.fireEvent("selectionchange", this, this.selections);
14652             }
14653         }
14654     },
14655
14656     /**
14657      * Returns true if the passed node is selected
14658      * @param {HTMLElement/Number} node The node or node index
14659      * @return {Boolean}
14660      */
14661     isSelected : function(node){
14662         var s = this.selections;
14663         if(s.length < 1){
14664             return false;
14665         }
14666         node = this.getNode(node);
14667         return s.indexOf(node) !== -1;
14668     },
14669
14670     /**
14671      * Selects nodes.
14672      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14673      * @param {Boolean} keepExisting (optional) true to keep existing selections
14674      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14675      */
14676     select : function(nodeInfo, keepExisting, suppressEvent){
14677         if(nodeInfo instanceof Array){
14678             if(!keepExisting){
14679                 this.clearSelections(true);
14680             }
14681             for(var i = 0, len = nodeInfo.length; i < len; i++){
14682                 this.select(nodeInfo[i], true, true);
14683             }
14684             return;
14685         } 
14686         var node = this.getNode(nodeInfo);
14687         if(!node || this.isSelected(node)){
14688             return; // already selected.
14689         }
14690         if(!keepExisting){
14691             this.clearSelections(true);
14692         }
14693         
14694         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
14695             Roo.fly(node).addClass(this.selectedClass);
14696             this.selections.push(node);
14697             if(!suppressEvent){
14698                 this.fireEvent("selectionchange", this, this.selections);
14699             }
14700         }
14701         
14702         
14703     },
14704       /**
14705      * Unselects nodes.
14706      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
14707      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
14708      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14709      */
14710     unselect : function(nodeInfo, keepExisting, suppressEvent)
14711     {
14712         if(nodeInfo instanceof Array){
14713             Roo.each(this.selections, function(s) {
14714                 this.unselect(s, nodeInfo);
14715             }, this);
14716             return;
14717         }
14718         var node = this.getNode(nodeInfo);
14719         if(!node || !this.isSelected(node)){
14720             //Roo.log("not selected");
14721             return; // not selected.
14722         }
14723         // fireevent???
14724         var ns = [];
14725         Roo.each(this.selections, function(s) {
14726             if (s == node ) {
14727                 Roo.fly(node).removeClass(this.selectedClass);
14728
14729                 return;
14730             }
14731             ns.push(s);
14732         },this);
14733         
14734         this.selections= ns;
14735         this.fireEvent("selectionchange", this, this.selections);
14736     },
14737
14738     /**
14739      * Gets a template node.
14740      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14741      * @return {HTMLElement} The node or null if it wasn't found
14742      */
14743     getNode : function(nodeInfo){
14744         if(typeof nodeInfo == "string"){
14745             return document.getElementById(nodeInfo);
14746         }else if(typeof nodeInfo == "number"){
14747             return this.nodes[nodeInfo];
14748         }
14749         return nodeInfo;
14750     },
14751
14752     /**
14753      * Gets a range template nodes.
14754      * @param {Number} startIndex
14755      * @param {Number} endIndex
14756      * @return {Array} An array of nodes
14757      */
14758     getNodes : function(start, end){
14759         var ns = this.nodes;
14760         start = start || 0;
14761         end = typeof end == "undefined" ? ns.length - 1 : end;
14762         var nodes = [];
14763         if(start <= end){
14764             for(var i = start; i <= end; i++){
14765                 nodes.push(ns[i]);
14766             }
14767         } else{
14768             for(var i = start; i >= end; i--){
14769                 nodes.push(ns[i]);
14770             }
14771         }
14772         return nodes;
14773     },
14774
14775     /**
14776      * Finds the index of the passed node
14777      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
14778      * @return {Number} The index of the node or -1
14779      */
14780     indexOf : function(node){
14781         node = this.getNode(node);
14782         if(typeof node.nodeIndex == "number"){
14783             return node.nodeIndex;
14784         }
14785         var ns = this.nodes;
14786         for(var i = 0, len = ns.length; i < len; i++){
14787             if(ns[i] == node){
14788                 return i;
14789             }
14790         }
14791         return -1;
14792     }
14793 });
14794 /*
14795  * - LGPL
14796  *
14797  * based on jquery fullcalendar
14798  * 
14799  */
14800
14801 Roo.bootstrap = Roo.bootstrap || {};
14802 /**
14803  * @class Roo.bootstrap.Calendar
14804  * @extends Roo.bootstrap.Component
14805  * Bootstrap Calendar class
14806  * @cfg {Boolean} loadMask (true|false) default false
14807  * @cfg {Object} header generate the user specific header of the calendar, default false
14808
14809  * @constructor
14810  * Create a new Container
14811  * @param {Object} config The config object
14812  */
14813
14814
14815
14816 Roo.bootstrap.Calendar = function(config){
14817     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
14818      this.addEvents({
14819         /**
14820              * @event select
14821              * Fires when a date is selected
14822              * @param {DatePicker} this
14823              * @param {Date} date The selected date
14824              */
14825         'select': true,
14826         /**
14827              * @event monthchange
14828              * Fires when the displayed month changes 
14829              * @param {DatePicker} this
14830              * @param {Date} date The selected month
14831              */
14832         'monthchange': true,
14833         /**
14834              * @event evententer
14835              * Fires when mouse over an event
14836              * @param {Calendar} this
14837              * @param {event} Event
14838              */
14839         'evententer': true,
14840         /**
14841              * @event eventleave
14842              * Fires when the mouse leaves an
14843              * @param {Calendar} this
14844              * @param {event}
14845              */
14846         'eventleave': true,
14847         /**
14848              * @event eventclick
14849              * Fires when the mouse click an
14850              * @param {Calendar} this
14851              * @param {event}
14852              */
14853         'eventclick': true
14854         
14855     });
14856
14857 };
14858
14859 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
14860     
14861      /**
14862      * @cfg {Number} startDay
14863      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
14864      */
14865     startDay : 0,
14866     
14867     loadMask : false,
14868     
14869     header : false,
14870       
14871     getAutoCreate : function(){
14872         
14873         
14874         var fc_button = function(name, corner, style, content ) {
14875             return Roo.apply({},{
14876                 tag : 'span',
14877                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
14878                          (corner.length ?
14879                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
14880                             ''
14881                         ),
14882                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
14883                 unselectable: 'on'
14884             });
14885         };
14886         
14887         var header = {};
14888         
14889         if(!this.header){
14890             header = {
14891                 tag : 'table',
14892                 cls : 'fc-header',
14893                 style : 'width:100%',
14894                 cn : [
14895                     {
14896                         tag: 'tr',
14897                         cn : [
14898                             {
14899                                 tag : 'td',
14900                                 cls : 'fc-header-left',
14901                                 cn : [
14902                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
14903                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
14904                                     { tag: 'span', cls: 'fc-header-space' },
14905                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
14906
14907
14908                                 ]
14909                             },
14910
14911                             {
14912                                 tag : 'td',
14913                                 cls : 'fc-header-center',
14914                                 cn : [
14915                                     {
14916                                         tag: 'span',
14917                                         cls: 'fc-header-title',
14918                                         cn : {
14919                                             tag: 'H2',
14920                                             html : 'month / year'
14921                                         }
14922                                     }
14923
14924                                 ]
14925                             },
14926                             {
14927                                 tag : 'td',
14928                                 cls : 'fc-header-right',
14929                                 cn : [
14930                               /*      fc_button('month', 'left', '', 'month' ),
14931                                     fc_button('week', '', '', 'week' ),
14932                                     fc_button('day', 'right', '', 'day' )
14933                                 */    
14934
14935                                 ]
14936                             }
14937
14938                         ]
14939                     }
14940                 ]
14941             };
14942         }
14943         
14944         header = this.header;
14945         
14946        
14947         var cal_heads = function() {
14948             var ret = [];
14949             // fixme - handle this.
14950             
14951             for (var i =0; i < Date.dayNames.length; i++) {
14952                 var d = Date.dayNames[i];
14953                 ret.push({
14954                     tag: 'th',
14955                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
14956                     html : d.substring(0,3)
14957                 });
14958                 
14959             }
14960             ret[0].cls += ' fc-first';
14961             ret[6].cls += ' fc-last';
14962             return ret;
14963         };
14964         var cal_cell = function(n) {
14965             return  {
14966                 tag: 'td',
14967                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
14968                 cn : [
14969                     {
14970                         cn : [
14971                             {
14972                                 cls: 'fc-day-number',
14973                                 html: 'D'
14974                             },
14975                             {
14976                                 cls: 'fc-day-content',
14977                              
14978                                 cn : [
14979                                      {
14980                                         style: 'position: relative;' // height: 17px;
14981                                     }
14982                                 ]
14983                             }
14984                             
14985                             
14986                         ]
14987                     }
14988                 ]
14989                 
14990             }
14991         };
14992         var cal_rows = function() {
14993             
14994             var ret = [];
14995             for (var r = 0; r < 6; r++) {
14996                 var row= {
14997                     tag : 'tr',
14998                     cls : 'fc-week',
14999                     cn : []
15000                 };
15001                 
15002                 for (var i =0; i < Date.dayNames.length; i++) {
15003                     var d = Date.dayNames[i];
15004                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15005
15006                 }
15007                 row.cn[0].cls+=' fc-first';
15008                 row.cn[0].cn[0].style = 'min-height:90px';
15009                 row.cn[6].cls+=' fc-last';
15010                 ret.push(row);
15011                 
15012             }
15013             ret[0].cls += ' fc-first';
15014             ret[4].cls += ' fc-prev-last';
15015             ret[5].cls += ' fc-last';
15016             return ret;
15017             
15018         };
15019         
15020         var cal_table = {
15021             tag: 'table',
15022             cls: 'fc-border-separate',
15023             style : 'width:100%',
15024             cellspacing  : 0,
15025             cn : [
15026                 { 
15027                     tag: 'thead',
15028                     cn : [
15029                         { 
15030                             tag: 'tr',
15031                             cls : 'fc-first fc-last',
15032                             cn : cal_heads()
15033                         }
15034                     ]
15035                 },
15036                 { 
15037                     tag: 'tbody',
15038                     cn : cal_rows()
15039                 }
15040                   
15041             ]
15042         };
15043          
15044          var cfg = {
15045             cls : 'fc fc-ltr',
15046             cn : [
15047                 header,
15048                 {
15049                     cls : 'fc-content',
15050                     style : "position: relative;",
15051                     cn : [
15052                         {
15053                             cls : 'fc-view fc-view-month fc-grid',
15054                             style : 'position: relative',
15055                             unselectable : 'on',
15056                             cn : [
15057                                 {
15058                                     cls : 'fc-event-container',
15059                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15060                                 },
15061                                 cal_table
15062                             ]
15063                         }
15064                     ]
15065     
15066                 }
15067            ] 
15068             
15069         };
15070         
15071          
15072         
15073         return cfg;
15074     },
15075     
15076     
15077     initEvents : function()
15078     {
15079         if(!this.store){
15080             throw "can not find store for calendar";
15081         }
15082         
15083         var mark = {
15084             tag: "div",
15085             cls:"x-dlg-mask",
15086             style: "text-align:center",
15087             cn: [
15088                 {
15089                     tag: "div",
15090                     style: "background-color:white;width:50%;margin:250 auto",
15091                     cn: [
15092                         {
15093                             tag: "img",
15094                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15095                         },
15096                         {
15097                             tag: "span",
15098                             html: "Loading"
15099                         }
15100                         
15101                     ]
15102                 }
15103             ]
15104         };
15105         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15106         
15107         var size = this.el.select('.fc-content', true).first().getSize();
15108         this.maskEl.setSize(size.width, size.height);
15109         this.maskEl.enableDisplayMode("block");
15110         if(!this.loadMask){
15111             this.maskEl.hide();
15112         }
15113         
15114         this.store = Roo.factory(this.store, Roo.data);
15115         this.store.on('load', this.onLoad, this);
15116         this.store.on('beforeload', this.onBeforeLoad, this);
15117         
15118         this.resize();
15119         
15120         this.cells = this.el.select('.fc-day',true);
15121         //Roo.log(this.cells);
15122         this.textNodes = this.el.query('.fc-day-number');
15123         this.cells.addClassOnOver('fc-state-hover');
15124         
15125         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15126         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15127         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15128         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15129         
15130         this.on('monthchange', this.onMonthChange, this);
15131         
15132         this.update(new Date().clearTime());
15133     },
15134     
15135     resize : function() {
15136         var sz  = this.el.getSize();
15137         
15138         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15139         this.el.select('.fc-day-content div',true).setHeight(34);
15140     },
15141     
15142     
15143     // private
15144     showPrevMonth : function(e){
15145         this.update(this.activeDate.add("mo", -1));
15146     },
15147     showToday : function(e){
15148         this.update(new Date().clearTime());
15149     },
15150     // private
15151     showNextMonth : function(e){
15152         this.update(this.activeDate.add("mo", 1));
15153     },
15154
15155     // private
15156     showPrevYear : function(){
15157         this.update(this.activeDate.add("y", -1));
15158     },
15159
15160     // private
15161     showNextYear : function(){
15162         this.update(this.activeDate.add("y", 1));
15163     },
15164
15165     
15166    // private
15167     update : function(date)
15168     {
15169         var vd = this.activeDate;
15170         this.activeDate = date;
15171 //        if(vd && this.el){
15172 //            var t = date.getTime();
15173 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15174 //                Roo.log('using add remove');
15175 //                
15176 //                this.fireEvent('monthchange', this, date);
15177 //                
15178 //                this.cells.removeClass("fc-state-highlight");
15179 //                this.cells.each(function(c){
15180 //                   if(c.dateValue == t){
15181 //                       c.addClass("fc-state-highlight");
15182 //                       setTimeout(function(){
15183 //                            try{c.dom.firstChild.focus();}catch(e){}
15184 //                       }, 50);
15185 //                       return false;
15186 //                   }
15187 //                   return true;
15188 //                });
15189 //                return;
15190 //            }
15191 //        }
15192         
15193         var days = date.getDaysInMonth();
15194         
15195         var firstOfMonth = date.getFirstDateOfMonth();
15196         var startingPos = firstOfMonth.getDay()-this.startDay;
15197         
15198         if(startingPos < this.startDay){
15199             startingPos += 7;
15200         }
15201         
15202         var pm = date.add(Date.MONTH, -1);
15203         var prevStart = pm.getDaysInMonth()-startingPos;
15204 //        
15205         this.cells = this.el.select('.fc-day',true);
15206         this.textNodes = this.el.query('.fc-day-number');
15207         this.cells.addClassOnOver('fc-state-hover');
15208         
15209         var cells = this.cells.elements;
15210         var textEls = this.textNodes;
15211         
15212         Roo.each(cells, function(cell){
15213             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15214         });
15215         
15216         days += startingPos;
15217
15218         // convert everything to numbers so it's fast
15219         var day = 86400000;
15220         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15221         //Roo.log(d);
15222         //Roo.log(pm);
15223         //Roo.log(prevStart);
15224         
15225         var today = new Date().clearTime().getTime();
15226         var sel = date.clearTime().getTime();
15227         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15228         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15229         var ddMatch = this.disabledDatesRE;
15230         var ddText = this.disabledDatesText;
15231         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15232         var ddaysText = this.disabledDaysText;
15233         var format = this.format;
15234         
15235         var setCellClass = function(cal, cell){
15236             cell.row = 0;
15237             cell.events = [];
15238             cell.more = [];
15239             //Roo.log('set Cell Class');
15240             cell.title = "";
15241             var t = d.getTime();
15242             
15243             //Roo.log(d);
15244             
15245             cell.dateValue = t;
15246             if(t == today){
15247                 cell.className += " fc-today";
15248                 cell.className += " fc-state-highlight";
15249                 cell.title = cal.todayText;
15250             }
15251             if(t == sel){
15252                 // disable highlight in other month..
15253                 //cell.className += " fc-state-highlight";
15254                 
15255             }
15256             // disabling
15257             if(t < min) {
15258                 cell.className = " fc-state-disabled";
15259                 cell.title = cal.minText;
15260                 return;
15261             }
15262             if(t > max) {
15263                 cell.className = " fc-state-disabled";
15264                 cell.title = cal.maxText;
15265                 return;
15266             }
15267             if(ddays){
15268                 if(ddays.indexOf(d.getDay()) != -1){
15269                     cell.title = ddaysText;
15270                     cell.className = " fc-state-disabled";
15271                 }
15272             }
15273             if(ddMatch && format){
15274                 var fvalue = d.dateFormat(format);
15275                 if(ddMatch.test(fvalue)){
15276                     cell.title = ddText.replace("%0", fvalue);
15277                     cell.className = " fc-state-disabled";
15278                 }
15279             }
15280             
15281             if (!cell.initialClassName) {
15282                 cell.initialClassName = cell.dom.className;
15283             }
15284             
15285             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15286         };
15287
15288         var i = 0;
15289         
15290         for(; i < startingPos; i++) {
15291             textEls[i].innerHTML = (++prevStart);
15292             d.setDate(d.getDate()+1);
15293             
15294             cells[i].className = "fc-past fc-other-month";
15295             setCellClass(this, cells[i]);
15296         }
15297         
15298         var intDay = 0;
15299         
15300         for(; i < days; i++){
15301             intDay = i - startingPos + 1;
15302             textEls[i].innerHTML = (intDay);
15303             d.setDate(d.getDate()+1);
15304             
15305             cells[i].className = ''; // "x-date-active";
15306             setCellClass(this, cells[i]);
15307         }
15308         var extraDays = 0;
15309         
15310         for(; i < 42; i++) {
15311             textEls[i].innerHTML = (++extraDays);
15312             d.setDate(d.getDate()+1);
15313             
15314             cells[i].className = "fc-future fc-other-month";
15315             setCellClass(this, cells[i]);
15316         }
15317         
15318         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15319         
15320         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15321         
15322         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15323         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15324         
15325         if(totalRows != 6){
15326             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15327             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15328         }
15329         
15330         this.fireEvent('monthchange', this, date);
15331         
15332         
15333         /*
15334         if(!this.internalRender){
15335             var main = this.el.dom.firstChild;
15336             var w = main.offsetWidth;
15337             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15338             Roo.fly(main).setWidth(w);
15339             this.internalRender = true;
15340             // opera does not respect the auto grow header center column
15341             // then, after it gets a width opera refuses to recalculate
15342             // without a second pass
15343             if(Roo.isOpera && !this.secondPass){
15344                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15345                 this.secondPass = true;
15346                 this.update.defer(10, this, [date]);
15347             }
15348         }
15349         */
15350         
15351     },
15352     
15353     findCell : function(dt) {
15354         dt = dt.clearTime().getTime();
15355         var ret = false;
15356         this.cells.each(function(c){
15357             //Roo.log("check " +c.dateValue + '?=' + dt);
15358             if(c.dateValue == dt){
15359                 ret = c;
15360                 return false;
15361             }
15362             return true;
15363         });
15364         
15365         return ret;
15366     },
15367     
15368     findCells : function(ev) {
15369         var s = ev.start.clone().clearTime().getTime();
15370        // Roo.log(s);
15371         var e= ev.end.clone().clearTime().getTime();
15372        // Roo.log(e);
15373         var ret = [];
15374         this.cells.each(function(c){
15375              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15376             
15377             if(c.dateValue > e){
15378                 return ;
15379             }
15380             if(c.dateValue < s){
15381                 return ;
15382             }
15383             ret.push(c);
15384         });
15385         
15386         return ret;    
15387     },
15388     
15389 //    findBestRow: function(cells)
15390 //    {
15391 //        var ret = 0;
15392 //        
15393 //        for (var i =0 ; i < cells.length;i++) {
15394 //            ret  = Math.max(cells[i].rows || 0,ret);
15395 //        }
15396 //        return ret;
15397 //        
15398 //    },
15399     
15400     
15401     addItem : function(ev)
15402     {
15403         // look for vertical location slot in
15404         var cells = this.findCells(ev);
15405         
15406 //        ev.row = this.findBestRow(cells);
15407         
15408         // work out the location.
15409         
15410         var crow = false;
15411         var rows = [];
15412         for(var i =0; i < cells.length; i++) {
15413             
15414             cells[i].row = cells[0].row;
15415             
15416             if(i == 0){
15417                 cells[i].row = cells[i].row + 1;
15418             }
15419             
15420             if (!crow) {
15421                 crow = {
15422                     start : cells[i],
15423                     end :  cells[i]
15424                 };
15425                 continue;
15426             }
15427             if (crow.start.getY() == cells[i].getY()) {
15428                 // on same row.
15429                 crow.end = cells[i];
15430                 continue;
15431             }
15432             // different row.
15433             rows.push(crow);
15434             crow = {
15435                 start: cells[i],
15436                 end : cells[i]
15437             };
15438             
15439         }
15440         
15441         rows.push(crow);
15442         ev.els = [];
15443         ev.rows = rows;
15444         ev.cells = cells;
15445         
15446         cells[0].events.push(ev);
15447         
15448         this.calevents.push(ev);
15449     },
15450     
15451     clearEvents: function() {
15452         
15453         if(!this.calevents){
15454             return;
15455         }
15456         
15457         Roo.each(this.cells.elements, function(c){
15458             c.row = 0;
15459             c.events = [];
15460             c.more = [];
15461         });
15462         
15463         Roo.each(this.calevents, function(e) {
15464             Roo.each(e.els, function(el) {
15465                 el.un('mouseenter' ,this.onEventEnter, this);
15466                 el.un('mouseleave' ,this.onEventLeave, this);
15467                 el.remove();
15468             },this);
15469         },this);
15470         
15471         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15472             e.remove();
15473         });
15474         
15475     },
15476     
15477     renderEvents: function()
15478     {   
15479         var _this = this;
15480         
15481         this.cells.each(function(c) {
15482             
15483             if(c.row < 5){
15484                 return;
15485             }
15486             
15487             var ev = c.events;
15488             
15489             var r = 4;
15490             if(c.row != c.events.length){
15491                 r = 4 - (4 - (c.row - c.events.length));
15492             }
15493             
15494             c.events = ev.slice(0, r);
15495             c.more = ev.slice(r);
15496             
15497             if(c.more.length && c.more.length == 1){
15498                 c.events.push(c.more.pop());
15499             }
15500             
15501             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15502             
15503         });
15504             
15505         this.cells.each(function(c) {
15506             
15507             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15508             
15509             
15510             for (var e = 0; e < c.events.length; e++){
15511                 var ev = c.events[e];
15512                 var rows = ev.rows;
15513                 
15514                 for(var i = 0; i < rows.length; i++) {
15515                 
15516                     // how many rows should it span..
15517
15518                     var  cfg = {
15519                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15520                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15521
15522                         unselectable : "on",
15523                         cn : [
15524                             {
15525                                 cls: 'fc-event-inner',
15526                                 cn : [
15527     //                                {
15528     //                                  tag:'span',
15529     //                                  cls: 'fc-event-time',
15530     //                                  html : cells.length > 1 ? '' : ev.time
15531     //                                },
15532                                     {
15533                                       tag:'span',
15534                                       cls: 'fc-event-title',
15535                                       html : String.format('{0}', ev.title)
15536                                     }
15537
15538
15539                                 ]
15540                             },
15541                             {
15542                                 cls: 'ui-resizable-handle ui-resizable-e',
15543                                 html : '&nbsp;&nbsp;&nbsp'
15544                             }
15545
15546                         ]
15547                     };
15548
15549                     if (i == 0) {
15550                         cfg.cls += ' fc-event-start';
15551                     }
15552                     if ((i+1) == rows.length) {
15553                         cfg.cls += ' fc-event-end';
15554                     }
15555
15556                     var ctr = _this.el.select('.fc-event-container',true).first();
15557                     var cg = ctr.createChild(cfg);
15558
15559                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15560                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15561
15562                     var r = (c.more.length) ? 1 : 0;
15563                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15564                     cg.setWidth(ebox.right - sbox.x -2);
15565
15566                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15567                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15568                     cg.on('click', _this.onEventClick, _this, ev);
15569
15570                     ev.els.push(cg);
15571                     
15572                 }
15573                 
15574             }
15575             
15576             
15577             if(c.more.length){
15578                 var  cfg = {
15579                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15580                     style : 'position: absolute',
15581                     unselectable : "on",
15582                     cn : [
15583                         {
15584                             cls: 'fc-event-inner',
15585                             cn : [
15586                                 {
15587                                   tag:'span',
15588                                   cls: 'fc-event-title',
15589                                   html : 'More'
15590                                 }
15591
15592
15593                             ]
15594                         },
15595                         {
15596                             cls: 'ui-resizable-handle ui-resizable-e',
15597                             html : '&nbsp;&nbsp;&nbsp'
15598                         }
15599
15600                     ]
15601                 };
15602
15603                 var ctr = _this.el.select('.fc-event-container',true).first();
15604                 var cg = ctr.createChild(cfg);
15605
15606                 var sbox = c.select('.fc-day-content',true).first().getBox();
15607                 var ebox = c.select('.fc-day-content',true).first().getBox();
15608                 //Roo.log(cg);
15609                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15610                 cg.setWidth(ebox.right - sbox.x -2);
15611
15612                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15613                 
15614             }
15615             
15616         });
15617         
15618         
15619         
15620     },
15621     
15622     onEventEnter: function (e, el,event,d) {
15623         this.fireEvent('evententer', this, el, event);
15624     },
15625     
15626     onEventLeave: function (e, el,event,d) {
15627         this.fireEvent('eventleave', this, el, event);
15628     },
15629     
15630     onEventClick: function (e, el,event,d) {
15631         this.fireEvent('eventclick', this, el, event);
15632     },
15633     
15634     onMonthChange: function () {
15635         this.store.load();
15636     },
15637     
15638     onMoreEventClick: function(e, el, more)
15639     {
15640         var _this = this;
15641         
15642         this.calpopover.placement = 'right';
15643         this.calpopover.setTitle('More');
15644         
15645         this.calpopover.setContent('');
15646         
15647         var ctr = this.calpopover.el.select('.popover-content', true).first();
15648         
15649         Roo.each(more, function(m){
15650             var cfg = {
15651                 cls : 'fc-event-hori fc-event-draggable',
15652                 html : m.title
15653             };
15654             var cg = ctr.createChild(cfg);
15655             
15656             cg.on('click', _this.onEventClick, _this, m);
15657         });
15658         
15659         this.calpopover.show(el);
15660         
15661         
15662     },
15663     
15664     onLoad: function () 
15665     {   
15666         this.calevents = [];
15667         var cal = this;
15668         
15669         if(this.store.getCount() > 0){
15670             this.store.data.each(function(d){
15671                cal.addItem({
15672                     id : d.data.id,
15673                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15674                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15675                     time : d.data.start_time,
15676                     title : d.data.title,
15677                     description : d.data.description,
15678                     venue : d.data.venue
15679                 });
15680             });
15681         }
15682         
15683         this.renderEvents();
15684         
15685         if(this.calevents.length && this.loadMask){
15686             this.maskEl.hide();
15687         }
15688     },
15689     
15690     onBeforeLoad: function()
15691     {
15692         this.clearEvents();
15693         if(this.loadMask){
15694             this.maskEl.show();
15695         }
15696     }
15697 });
15698
15699  
15700  /*
15701  * - LGPL
15702  *
15703  * element
15704  * 
15705  */
15706
15707 /**
15708  * @class Roo.bootstrap.Popover
15709  * @extends Roo.bootstrap.Component
15710  * Bootstrap Popover class
15711  * @cfg {String} html contents of the popover   (or false to use children..)
15712  * @cfg {String} title of popover (or false to hide)
15713  * @cfg {String} placement how it is placed
15714  * @cfg {String} trigger click || hover (or false to trigger manually)
15715  * @cfg {String} over what (parent or false to trigger manually.)
15716  * @cfg {Number} delay - delay before showing
15717  
15718  * @constructor
15719  * Create a new Popover
15720  * @param {Object} config The config object
15721  */
15722
15723 Roo.bootstrap.Popover = function(config){
15724     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
15725     
15726     this.addEvents({
15727         // raw events
15728          /**
15729          * @event show
15730          * After the popover show
15731          * 
15732          * @param {Roo.bootstrap.Popover} this
15733          */
15734         "show" : true,
15735         /**
15736          * @event hide
15737          * After the popover hide
15738          * 
15739          * @param {Roo.bootstrap.Popover} this
15740          */
15741         "hide" : true
15742     });
15743 };
15744
15745 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
15746     
15747     title: 'Fill in a title',
15748     html: false,
15749     
15750     placement : 'right',
15751     trigger : 'hover', // hover
15752     
15753     delay : 0,
15754     
15755     over: 'parent',
15756     
15757     can_build_overlaid : false,
15758     
15759     getChildContainer : function()
15760     {
15761         return this.el.select('.popover-content',true).first();
15762     },
15763     
15764     getAutoCreate : function(){
15765          
15766         var cfg = {
15767            cls : 'popover roo-dynamic',
15768            style: 'display:block',
15769            cn : [
15770                 {
15771                     cls : 'arrow'
15772                 },
15773                 {
15774                     cls : 'popover-inner',
15775                     cn : [
15776                         {
15777                             tag: 'h3',
15778                             cls: 'popover-title',
15779                             html : this.title
15780                         },
15781                         {
15782                             cls : 'popover-content',
15783                             html : this.html
15784                         }
15785                     ]
15786                     
15787                 }
15788            ]
15789         };
15790         
15791         return cfg;
15792     },
15793     setTitle: function(str)
15794     {
15795         this.title = str;
15796         this.el.select('.popover-title',true).first().dom.innerHTML = str;
15797     },
15798     setContent: function(str)
15799     {
15800         this.html = str;
15801         this.el.select('.popover-content',true).first().dom.innerHTML = str;
15802     },
15803     // as it get's added to the bottom of the page.
15804     onRender : function(ct, position)
15805     {
15806         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15807         if(!this.el){
15808             var cfg = Roo.apply({},  this.getAutoCreate());
15809             cfg.id = Roo.id();
15810             
15811             if (this.cls) {
15812                 cfg.cls += ' ' + this.cls;
15813             }
15814             if (this.style) {
15815                 cfg.style = this.style;
15816             }
15817             //Roo.log("adding to ");
15818             this.el = Roo.get(document.body).createChild(cfg, position);
15819 //            Roo.log(this.el);
15820         }
15821         this.initEvents();
15822     },
15823     
15824     initEvents : function()
15825     {
15826         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
15827         this.el.enableDisplayMode('block');
15828         this.el.hide();
15829         if (this.over === false) {
15830             return; 
15831         }
15832         if (this.triggers === false) {
15833             return;
15834         }
15835         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15836         var triggers = this.trigger ? this.trigger.split(' ') : [];
15837         Roo.each(triggers, function(trigger) {
15838         
15839             if (trigger == 'click') {
15840                 on_el.on('click', this.toggle, this);
15841             } else if (trigger != 'manual') {
15842                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
15843                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
15844       
15845                 on_el.on(eventIn  ,this.enter, this);
15846                 on_el.on(eventOut, this.leave, this);
15847             }
15848         }, this);
15849         
15850     },
15851     
15852     
15853     // private
15854     timeout : null,
15855     hoverState : null,
15856     
15857     toggle : function () {
15858         this.hoverState == 'in' ? this.leave() : this.enter();
15859     },
15860     
15861     enter : function () {
15862        
15863     
15864         clearTimeout(this.timeout);
15865     
15866         this.hoverState = 'in';
15867     
15868         if (!this.delay || !this.delay.show) {
15869             this.show();
15870             return;
15871         }
15872         var _t = this;
15873         this.timeout = setTimeout(function () {
15874             if (_t.hoverState == 'in') {
15875                 _t.show();
15876             }
15877         }, this.delay.show)
15878     },
15879     leave : function() {
15880         clearTimeout(this.timeout);
15881     
15882         this.hoverState = 'out';
15883     
15884         if (!this.delay || !this.delay.hide) {
15885             this.hide();
15886             return;
15887         }
15888         var _t = this;
15889         this.timeout = setTimeout(function () {
15890             if (_t.hoverState == 'out') {
15891                 _t.hide();
15892             }
15893         }, this.delay.hide)
15894     },
15895     
15896     show : function (on_el)
15897     {
15898         if (!on_el) {
15899             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
15900         }
15901         // set content.
15902         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
15903         if (this.html !== false) {
15904             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
15905         }
15906         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
15907         if (!this.title.length) {
15908             this.el.select('.popover-title',true).hide();
15909         }
15910         
15911         var placement = typeof this.placement == 'function' ?
15912             this.placement.call(this, this.el, on_el) :
15913             this.placement;
15914             
15915         var autoToken = /\s?auto?\s?/i;
15916         var autoPlace = autoToken.test(placement);
15917         if (autoPlace) {
15918             placement = placement.replace(autoToken, '') || 'top';
15919         }
15920         
15921         //this.el.detach()
15922         //this.el.setXY([0,0]);
15923         this.el.show();
15924         this.el.dom.style.display='block';
15925         this.el.addClass(placement);
15926         
15927         //this.el.appendTo(on_el);
15928         
15929         var p = this.getPosition();
15930         var box = this.el.getBox();
15931         
15932         if (autoPlace) {
15933             // fixme..
15934         }
15935         var align = Roo.bootstrap.Popover.alignment[placement];
15936         this.el.alignTo(on_el, align[0],align[1]);
15937         //var arrow = this.el.select('.arrow',true).first();
15938         //arrow.set(align[2], 
15939         
15940         this.el.addClass('in');
15941         
15942         
15943         if (this.el.hasClass('fade')) {
15944             // fade it?
15945         }
15946         
15947         this.fireEvent('show', this);
15948         
15949     },
15950     hide : function()
15951     {
15952         this.el.setXY([0,0]);
15953         this.el.removeClass('in');
15954         this.el.hide();
15955         this.hoverState = null;
15956         
15957         this.fireEvent('hide', this);
15958     }
15959     
15960 });
15961
15962 Roo.bootstrap.Popover.alignment = {
15963     'left' : ['r-l', [-10,0], 'right'],
15964     'right' : ['l-r', [10,0], 'left'],
15965     'bottom' : ['t-b', [0,10], 'top'],
15966     'top' : [ 'b-t', [0,-10], 'bottom']
15967 };
15968
15969  /*
15970  * - LGPL
15971  *
15972  * Progress
15973  * 
15974  */
15975
15976 /**
15977  * @class Roo.bootstrap.Progress
15978  * @extends Roo.bootstrap.Component
15979  * Bootstrap Progress class
15980  * @cfg {Boolean} striped striped of the progress bar
15981  * @cfg {Boolean} active animated of the progress bar
15982  * 
15983  * 
15984  * @constructor
15985  * Create a new Progress
15986  * @param {Object} config The config object
15987  */
15988
15989 Roo.bootstrap.Progress = function(config){
15990     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
15991 };
15992
15993 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
15994     
15995     striped : false,
15996     active: false,
15997     
15998     getAutoCreate : function(){
15999         var cfg = {
16000             tag: 'div',
16001             cls: 'progress'
16002         };
16003         
16004         
16005         if(this.striped){
16006             cfg.cls += ' progress-striped';
16007         }
16008       
16009         if(this.active){
16010             cfg.cls += ' active';
16011         }
16012         
16013         
16014         return cfg;
16015     }
16016    
16017 });
16018
16019  
16020
16021  /*
16022  * - LGPL
16023  *
16024  * ProgressBar
16025  * 
16026  */
16027
16028 /**
16029  * @class Roo.bootstrap.ProgressBar
16030  * @extends Roo.bootstrap.Component
16031  * Bootstrap ProgressBar class
16032  * @cfg {Number} aria_valuenow aria-value now
16033  * @cfg {Number} aria_valuemin aria-value min
16034  * @cfg {Number} aria_valuemax aria-value max
16035  * @cfg {String} label label for the progress bar
16036  * @cfg {String} panel (success | info | warning | danger )
16037  * @cfg {String} role role of the progress bar
16038  * @cfg {String} sr_only text
16039  * 
16040  * 
16041  * @constructor
16042  * Create a new ProgressBar
16043  * @param {Object} config The config object
16044  */
16045
16046 Roo.bootstrap.ProgressBar = function(config){
16047     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16048 };
16049
16050 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16051     
16052     aria_valuenow : 0,
16053     aria_valuemin : 0,
16054     aria_valuemax : 100,
16055     label : false,
16056     panel : false,
16057     role : false,
16058     sr_only: false,
16059     
16060     getAutoCreate : function()
16061     {
16062         
16063         var cfg = {
16064             tag: 'div',
16065             cls: 'progress-bar',
16066             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16067         };
16068         
16069         if(this.sr_only){
16070             cfg.cn = {
16071                 tag: 'span',
16072                 cls: 'sr-only',
16073                 html: this.sr_only
16074             }
16075         }
16076         
16077         if(this.role){
16078             cfg.role = this.role;
16079         }
16080         
16081         if(this.aria_valuenow){
16082             cfg['aria-valuenow'] = this.aria_valuenow;
16083         }
16084         
16085         if(this.aria_valuemin){
16086             cfg['aria-valuemin'] = this.aria_valuemin;
16087         }
16088         
16089         if(this.aria_valuemax){
16090             cfg['aria-valuemax'] = this.aria_valuemax;
16091         }
16092         
16093         if(this.label && !this.sr_only){
16094             cfg.html = this.label;
16095         }
16096         
16097         if(this.panel){
16098             cfg.cls += ' progress-bar-' + this.panel;
16099         }
16100         
16101         return cfg;
16102     },
16103     
16104     update : function(aria_valuenow)
16105     {
16106         this.aria_valuenow = aria_valuenow;
16107         
16108         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16109     }
16110    
16111 });
16112
16113  
16114
16115  /*
16116  * - LGPL
16117  *
16118  * column
16119  * 
16120  */
16121
16122 /**
16123  * @class Roo.bootstrap.TabGroup
16124  * @extends Roo.bootstrap.Column
16125  * Bootstrap Column class
16126  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16127  * @cfg {Boolean} carousel true to make the group behave like a carousel
16128  * @cfg {Boolean} bullets show bullets for the panels
16129  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16130  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16131  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16132  * 
16133  * @constructor
16134  * Create a new TabGroup
16135  * @param {Object} config The config object
16136  */
16137
16138 Roo.bootstrap.TabGroup = function(config){
16139     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16140     if (!this.navId) {
16141         this.navId = Roo.id();
16142     }
16143     this.tabs = [];
16144     Roo.bootstrap.TabGroup.register(this);
16145     
16146 };
16147
16148 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16149     
16150     carousel : false,
16151     transition : false,
16152     bullets : 0,
16153     timer : 0,
16154     autoslide : false,
16155     slideFn : false,
16156     slideOnTouch : false,
16157     
16158     getAutoCreate : function()
16159     {
16160         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16161         
16162         cfg.cls += ' tab-content';
16163         
16164         if (this.carousel) {
16165             cfg.cls += ' carousel slide';
16166             
16167             cfg.cn = [{
16168                cls : 'carousel-inner'
16169             }];
16170         
16171             if(this.bullets  && !Roo.isTouch){
16172                 
16173                 var bullets = {
16174                     cls : 'carousel-bullets',
16175                     cn : []
16176                 };
16177                
16178                 if(this.bullets_cls){
16179                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16180                 }
16181                  /*
16182                 for (var i = 0; i < this.bullets; i++){
16183                     bullets.cn.push({
16184                         cls : 'bullet bullet-' + i
16185                     });
16186                 }
16187                 */
16188                 bullets.cn.push({
16189                     cls : 'clear'
16190                 });
16191                 
16192                 cfg.cn[0].cn = bullets;
16193             }
16194         }
16195         
16196         return cfg;
16197     },
16198     
16199     initEvents:  function()
16200     {
16201         if(Roo.isTouch && this.slideOnTouch){
16202             this.el.on("touchstart", this.onTouchStart, this);
16203         }
16204         
16205         if(this.autoslide){
16206             var _this = this;
16207             
16208             this.slideFn = window.setInterval(function() {
16209                 _this.showPanelNext();
16210             }, this.timer);
16211         }
16212         
16213     },
16214     
16215     onTouchStart : function(e, el, o)
16216     {
16217         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16218             return;
16219         }
16220         
16221         this.showPanelNext();
16222     },
16223     
16224     getChildContainer : function()
16225     {
16226         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16227     },
16228     
16229     /**
16230     * register a Navigation item
16231     * @param {Roo.bootstrap.NavItem} the navitem to add
16232     */
16233     register : function(item)
16234     {
16235         this.tabs.push( item);
16236         item.navId = this.navId; // not really needed..
16237         this.addBullet();
16238     
16239     },
16240     
16241     getActivePanel : function()
16242     {
16243         var r = false;
16244         Roo.each(this.tabs, function(t) {
16245             if (t.active) {
16246                 r = t;
16247                 return false;
16248             }
16249             return null;
16250         });
16251         return r;
16252         
16253     },
16254     getPanelByName : function(n)
16255     {
16256         var r = false;
16257         Roo.each(this.tabs, function(t) {
16258             if (t.tabId == n) {
16259                 r = t;
16260                 return false;
16261             }
16262             return null;
16263         });
16264         return r;
16265     },
16266     indexOfPanel : function(p)
16267     {
16268         var r = false;
16269         Roo.each(this.tabs, function(t,i) {
16270             if (t.tabId == p.tabId) {
16271                 r = i;
16272                 return false;
16273             }
16274             return null;
16275         });
16276         return r;
16277     },
16278     /**
16279      * show a specific panel
16280      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16281      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16282      */
16283     showPanel : function (pan)
16284     {
16285         if(this.transition || typeof(pan) == 'undefined'){
16286             Roo.log("waiting for the transitionend");
16287             return;
16288         }
16289         
16290         if (typeof(pan) == 'number') {
16291             pan = this.tabs[pan];
16292         }
16293         
16294         if (typeof(pan) == 'string') {
16295             pan = this.getPanelByName(pan);
16296         }
16297         
16298         var cur = this.getActivePanel();
16299         
16300         if(!pan || !cur){
16301             Roo.log('pan or acitve pan is undefined');
16302             return false;
16303         }
16304         
16305         if (pan.tabId == this.getActivePanel().tabId) {
16306             return true;
16307         }
16308         
16309         if (false === cur.fireEvent('beforedeactivate')) {
16310             return false;
16311         }
16312         
16313         if(this.bullets > 0 && !Roo.isTouch){
16314             this.setActiveBullet(this.indexOfPanel(pan));
16315         }
16316         
16317         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16318             
16319             this.transition = true;
16320             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16321             var lr = dir == 'next' ? 'left' : 'right';
16322             pan.el.addClass(dir); // or prev
16323             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16324             cur.el.addClass(lr); // or right
16325             pan.el.addClass(lr);
16326             
16327             var _this = this;
16328             cur.el.on('transitionend', function() {
16329                 Roo.log("trans end?");
16330                 
16331                 pan.el.removeClass([lr,dir]);
16332                 pan.setActive(true);
16333                 
16334                 cur.el.removeClass([lr]);
16335                 cur.setActive(false);
16336                 
16337                 _this.transition = false;
16338                 
16339             }, this, { single:  true } );
16340             
16341             return true;
16342         }
16343         
16344         cur.setActive(false);
16345         pan.setActive(true);
16346         
16347         return true;
16348         
16349     },
16350     showPanelNext : function()
16351     {
16352         var i = this.indexOfPanel(this.getActivePanel());
16353         
16354         if (i >= this.tabs.length - 1 && !this.autoslide) {
16355             return;
16356         }
16357         
16358         if (i >= this.tabs.length - 1 && this.autoslide) {
16359             i = -1;
16360         }
16361         
16362         this.showPanel(this.tabs[i+1]);
16363     },
16364     
16365     showPanelPrev : function()
16366     {
16367         var i = this.indexOfPanel(this.getActivePanel());
16368         
16369         if (i  < 1 && !this.autoslide) {
16370             return;
16371         }
16372         
16373         if (i < 1 && this.autoslide) {
16374             i = this.tabs.length;
16375         }
16376         
16377         this.showPanel(this.tabs[i-1]);
16378     },
16379     
16380     
16381     addBullet: function()
16382     {
16383         if(!this.bullets || Roo.isTouch){
16384             return;
16385         }
16386         var ctr = this.el.select('.carousel-bullets',true).first();
16387         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16388         var bullet = ctr.createChild({
16389             cls : 'bullet bullet-' + i
16390         },ctr.dom.lastChild);
16391         
16392         
16393         var _this = this;
16394         
16395         bullet.on('click', (function(e, el, o, ii, t){
16396
16397             e.preventDefault();
16398
16399             this.showPanel(ii);
16400
16401             if(this.autoslide && this.slideFn){
16402                 clearInterval(this.slideFn);
16403                 this.slideFn = window.setInterval(function() {
16404                     _this.showPanelNext();
16405                 }, this.timer);
16406             }
16407
16408         }).createDelegate(this, [i, bullet], true));
16409                 
16410         
16411     },
16412      
16413     setActiveBullet : function(i)
16414     {
16415         if(Roo.isTouch){
16416             return;
16417         }
16418         
16419         Roo.each(this.el.select('.bullet', true).elements, function(el){
16420             el.removeClass('selected');
16421         });
16422
16423         var bullet = this.el.select('.bullet-' + i, true).first();
16424         
16425         if(!bullet){
16426             return;
16427         }
16428         
16429         bullet.addClass('selected');
16430     }
16431     
16432     
16433   
16434 });
16435
16436  
16437
16438  
16439  
16440 Roo.apply(Roo.bootstrap.TabGroup, {
16441     
16442     groups: {},
16443      /**
16444     * register a Navigation Group
16445     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16446     */
16447     register : function(navgrp)
16448     {
16449         this.groups[navgrp.navId] = navgrp;
16450         
16451     },
16452     /**
16453     * fetch a Navigation Group based on the navigation ID
16454     * if one does not exist , it will get created.
16455     * @param {string} the navgroup to add
16456     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16457     */
16458     get: function(navId) {
16459         if (typeof(this.groups[navId]) == 'undefined') {
16460             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16461         }
16462         return this.groups[navId] ;
16463     }
16464     
16465     
16466     
16467 });
16468
16469  /*
16470  * - LGPL
16471  *
16472  * TabPanel
16473  * 
16474  */
16475
16476 /**
16477  * @class Roo.bootstrap.TabPanel
16478  * @extends Roo.bootstrap.Component
16479  * Bootstrap TabPanel class
16480  * @cfg {Boolean} active panel active
16481  * @cfg {String} html panel content
16482  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16483  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16484  * 
16485  * 
16486  * @constructor
16487  * Create a new TabPanel
16488  * @param {Object} config The config object
16489  */
16490
16491 Roo.bootstrap.TabPanel = function(config){
16492     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16493     this.addEvents({
16494         /**
16495              * @event changed
16496              * Fires when the active status changes
16497              * @param {Roo.bootstrap.TabPanel} this
16498              * @param {Boolean} state the new state
16499             
16500          */
16501         'changed': true,
16502         /**
16503              * @event beforedeactivate
16504              * Fires before a tab is de-activated - can be used to do validation on a form.
16505              * @param {Roo.bootstrap.TabPanel} this
16506              * @return {Boolean} false if there is an error
16507             
16508          */
16509         'beforedeactivate': true
16510      });
16511     
16512     this.tabId = this.tabId || Roo.id();
16513   
16514 };
16515
16516 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16517     
16518     active: false,
16519     html: false,
16520     tabId: false,
16521     navId : false,
16522     
16523     getAutoCreate : function(){
16524         var cfg = {
16525             tag: 'div',
16526             // item is needed for carousel - not sure if it has any effect otherwise
16527             cls: 'tab-pane item',
16528             html: this.html || ''
16529         };
16530         
16531         if(this.active){
16532             cfg.cls += ' active';
16533         }
16534         
16535         if(this.tabId){
16536             cfg.tabId = this.tabId;
16537         }
16538         
16539         
16540         return cfg;
16541     },
16542     
16543     initEvents:  function()
16544     {
16545         var p = this.parent();
16546         this.navId = this.navId || p.navId;
16547         
16548         if (typeof(this.navId) != 'undefined') {
16549             // not really needed.. but just in case.. parent should be a NavGroup.
16550             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16551             
16552             tg.register(this);
16553             
16554             var i = tg.tabs.length - 1;
16555             
16556             if(this.active && tg.bullets > 0 && i < tg.bullets){
16557                 tg.setActiveBullet(i);
16558             }
16559         }
16560         
16561     },
16562     
16563     
16564     onRender : function(ct, position)
16565     {
16566        // Roo.log("Call onRender: " + this.xtype);
16567         
16568         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16569         
16570         
16571         
16572         
16573         
16574     },
16575     
16576     setActive: function(state)
16577     {
16578         Roo.log("panel - set active " + this.tabId + "=" + state);
16579         
16580         this.active = state;
16581         if (!state) {
16582             this.el.removeClass('active');
16583             
16584         } else  if (!this.el.hasClass('active')) {
16585             this.el.addClass('active');
16586         }
16587         
16588         this.fireEvent('changed', this, state);
16589     }
16590     
16591     
16592 });
16593  
16594
16595  
16596
16597  /*
16598  * - LGPL
16599  *
16600  * DateField
16601  * 
16602  */
16603
16604 /**
16605  * @class Roo.bootstrap.DateField
16606  * @extends Roo.bootstrap.Input
16607  * Bootstrap DateField class
16608  * @cfg {Number} weekStart default 0
16609  * @cfg {String} viewMode default empty, (months|years)
16610  * @cfg {String} minViewMode default empty, (months|years)
16611  * @cfg {Number} startDate default -Infinity
16612  * @cfg {Number} endDate default Infinity
16613  * @cfg {Boolean} todayHighlight default false
16614  * @cfg {Boolean} todayBtn default false
16615  * @cfg {Boolean} calendarWeeks default false
16616  * @cfg {Object} daysOfWeekDisabled default empty
16617  * @cfg {Boolean} singleMode default false (true | false)
16618  * 
16619  * @cfg {Boolean} keyboardNavigation default true
16620  * @cfg {String} language default en
16621  * 
16622  * @constructor
16623  * Create a new DateField
16624  * @param {Object} config The config object
16625  */
16626
16627 Roo.bootstrap.DateField = function(config){
16628     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16629      this.addEvents({
16630             /**
16631              * @event show
16632              * Fires when this field show.
16633              * @param {Roo.bootstrap.DateField} this
16634              * @param {Mixed} date The date value
16635              */
16636             show : true,
16637             /**
16638              * @event show
16639              * Fires when this field hide.
16640              * @param {Roo.bootstrap.DateField} this
16641              * @param {Mixed} date The date value
16642              */
16643             hide : true,
16644             /**
16645              * @event select
16646              * Fires when select a date.
16647              * @param {Roo.bootstrap.DateField} this
16648              * @param {Mixed} date The date value
16649              */
16650             select : true,
16651             /**
16652              * @event beforeselect
16653              * Fires when before select a date.
16654              * @param {Roo.bootstrap.DateField} this
16655              * @param {Mixed} date The date value
16656              */
16657             beforeselect : true
16658         });
16659 };
16660
16661 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16662     
16663     /**
16664      * @cfg {String} format
16665      * The default date format string which can be overriden for localization support.  The format must be
16666      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16667      */
16668     format : "m/d/y",
16669     /**
16670      * @cfg {String} altFormats
16671      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16672      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16673      */
16674     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16675     
16676     weekStart : 0,
16677     
16678     viewMode : '',
16679     
16680     minViewMode : '',
16681     
16682     todayHighlight : false,
16683     
16684     todayBtn: false,
16685     
16686     language: 'en',
16687     
16688     keyboardNavigation: true,
16689     
16690     calendarWeeks: false,
16691     
16692     startDate: -Infinity,
16693     
16694     endDate: Infinity,
16695     
16696     daysOfWeekDisabled: [],
16697     
16698     _events: [],
16699     
16700     singleMode : false,
16701     
16702     UTCDate: function()
16703     {
16704         return new Date(Date.UTC.apply(Date, arguments));
16705     },
16706     
16707     UTCToday: function()
16708     {
16709         var today = new Date();
16710         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
16711     },
16712     
16713     getDate: function() {
16714             var d = this.getUTCDate();
16715             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
16716     },
16717     
16718     getUTCDate: function() {
16719             return this.date;
16720     },
16721     
16722     setDate: function(d) {
16723             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
16724     },
16725     
16726     setUTCDate: function(d) {
16727             this.date = d;
16728             this.setValue(this.formatDate(this.date));
16729     },
16730         
16731     onRender: function(ct, position)
16732     {
16733         
16734         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
16735         
16736         this.language = this.language || 'en';
16737         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
16738         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
16739         
16740         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
16741         this.format = this.format || 'm/d/y';
16742         this.isInline = false;
16743         this.isInput = true;
16744         this.component = this.el.select('.add-on', true).first() || false;
16745         this.component = (this.component && this.component.length === 0) ? false : this.component;
16746         this.hasInput = this.component && this.inputEL().length;
16747         
16748         if (typeof(this.minViewMode === 'string')) {
16749             switch (this.minViewMode) {
16750                 case 'months':
16751                     this.minViewMode = 1;
16752                     break;
16753                 case 'years':
16754                     this.minViewMode = 2;
16755                     break;
16756                 default:
16757                     this.minViewMode = 0;
16758                     break;
16759             }
16760         }
16761         
16762         if (typeof(this.viewMode === 'string')) {
16763             switch (this.viewMode) {
16764                 case 'months':
16765                     this.viewMode = 1;
16766                     break;
16767                 case 'years':
16768                     this.viewMode = 2;
16769                     break;
16770                 default:
16771                     this.viewMode = 0;
16772                     break;
16773             }
16774         }
16775                 
16776         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
16777         
16778 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
16779         
16780         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
16781         
16782         this.picker().on('mousedown', this.onMousedown, this);
16783         this.picker().on('click', this.onClick, this);
16784         
16785         this.picker().addClass('datepicker-dropdown');
16786         
16787         this.startViewMode = this.viewMode;
16788         
16789         if(this.singleMode){
16790             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
16791                 v.setVisibilityMode(Roo.Element.DISPLAY);
16792                 v.hide();
16793             });
16794             
16795             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
16796                 v.setStyle('width', '189px');
16797             });
16798         }
16799         
16800         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
16801             if(!this.calendarWeeks){
16802                 v.remove();
16803                 return;
16804             }
16805             
16806             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16807             v.attr('colspan', function(i, val){
16808                 return parseInt(val) + 1;
16809             });
16810         });
16811                         
16812         
16813         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
16814         
16815         this.setStartDate(this.startDate);
16816         this.setEndDate(this.endDate);
16817         
16818         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
16819         
16820         this.fillDow();
16821         this.fillMonths();
16822         this.update();
16823         this.showMode();
16824         
16825         if(this.isInline) {
16826             this.show();
16827         }
16828     },
16829     
16830     picker : function()
16831     {
16832         return this.pickerEl;
16833 //        return this.el.select('.datepicker', true).first();
16834     },
16835     
16836     fillDow: function()
16837     {
16838         var dowCnt = this.weekStart;
16839         
16840         var dow = {
16841             tag: 'tr',
16842             cn: [
16843                 
16844             ]
16845         };
16846         
16847         if(this.calendarWeeks){
16848             dow.cn.push({
16849                 tag: 'th',
16850                 cls: 'cw',
16851                 html: '&nbsp;'
16852             })
16853         }
16854         
16855         while (dowCnt < this.weekStart + 7) {
16856             dow.cn.push({
16857                 tag: 'th',
16858                 cls: 'dow',
16859                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
16860             });
16861         }
16862         
16863         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
16864     },
16865     
16866     fillMonths: function()
16867     {    
16868         var i = 0;
16869         var months = this.picker().select('>.datepicker-months td', true).first();
16870         
16871         months.dom.innerHTML = '';
16872         
16873         while (i < 12) {
16874             var month = {
16875                 tag: 'span',
16876                 cls: 'month',
16877                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
16878             };
16879             
16880             months.createChild(month);
16881         }
16882         
16883     },
16884     
16885     update: function()
16886     {
16887         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
16888         
16889         if (this.date < this.startDate) {
16890             this.viewDate = new Date(this.startDate);
16891         } else if (this.date > this.endDate) {
16892             this.viewDate = new Date(this.endDate);
16893         } else {
16894             this.viewDate = new Date(this.date);
16895         }
16896         
16897         this.fill();
16898     },
16899     
16900     fill: function() 
16901     {
16902         var d = new Date(this.viewDate),
16903                 year = d.getUTCFullYear(),
16904                 month = d.getUTCMonth(),
16905                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
16906                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
16907                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
16908                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
16909                 currentDate = this.date && this.date.valueOf(),
16910                 today = this.UTCToday();
16911         
16912         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
16913         
16914 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
16915         
16916 //        this.picker.select('>tfoot th.today').
16917 //                                              .text(dates[this.language].today)
16918 //                                              .toggle(this.todayBtn !== false);
16919     
16920         this.updateNavArrows();
16921         this.fillMonths();
16922                                                 
16923         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
16924         
16925         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
16926          
16927         prevMonth.setUTCDate(day);
16928         
16929         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
16930         
16931         var nextMonth = new Date(prevMonth);
16932         
16933         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
16934         
16935         nextMonth = nextMonth.valueOf();
16936         
16937         var fillMonths = false;
16938         
16939         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
16940         
16941         while(prevMonth.valueOf() < nextMonth) {
16942             var clsName = '';
16943             
16944             if (prevMonth.getUTCDay() === this.weekStart) {
16945                 if(fillMonths){
16946                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
16947                 }
16948                     
16949                 fillMonths = {
16950                     tag: 'tr',
16951                     cn: []
16952                 };
16953                 
16954                 if(this.calendarWeeks){
16955                     // ISO 8601: First week contains first thursday.
16956                     // ISO also states week starts on Monday, but we can be more abstract here.
16957                     var
16958                     // Start of current week: based on weekstart/current date
16959                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
16960                     // Thursday of this week
16961                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
16962                     // First Thursday of year, year from thursday
16963                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
16964                     // Calendar week: ms between thursdays, div ms per day, div 7 days
16965                     calWeek =  (th - yth) / 864e5 / 7 + 1;
16966                     
16967                     fillMonths.cn.push({
16968                         tag: 'td',
16969                         cls: 'cw',
16970                         html: calWeek
16971                     });
16972                 }
16973             }
16974             
16975             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
16976                 clsName += ' old';
16977             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
16978                 clsName += ' new';
16979             }
16980             if (this.todayHighlight &&
16981                 prevMonth.getUTCFullYear() == today.getFullYear() &&
16982                 prevMonth.getUTCMonth() == today.getMonth() &&
16983                 prevMonth.getUTCDate() == today.getDate()) {
16984                 clsName += ' today';
16985             }
16986             
16987             if (currentDate && prevMonth.valueOf() === currentDate) {
16988                 clsName += ' active';
16989             }
16990             
16991             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
16992                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
16993                     clsName += ' disabled';
16994             }
16995             
16996             fillMonths.cn.push({
16997                 tag: 'td',
16998                 cls: 'day ' + clsName,
16999                 html: prevMonth.getDate()
17000             });
17001             
17002             prevMonth.setDate(prevMonth.getDate()+1);
17003         }
17004           
17005         var currentYear = this.date && this.date.getUTCFullYear();
17006         var currentMonth = this.date && this.date.getUTCMonth();
17007         
17008         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17009         
17010         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17011             v.removeClass('active');
17012             
17013             if(currentYear === year && k === currentMonth){
17014                 v.addClass('active');
17015             }
17016             
17017             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17018                 v.addClass('disabled');
17019             }
17020             
17021         });
17022         
17023         
17024         year = parseInt(year/10, 10) * 10;
17025         
17026         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17027         
17028         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17029         
17030         year -= 1;
17031         for (var i = -1; i < 11; i++) {
17032             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17033                 tag: 'span',
17034                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17035                 html: year
17036             });
17037             
17038             year += 1;
17039         }
17040     },
17041     
17042     showMode: function(dir) 
17043     {
17044         if (dir) {
17045             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17046         }
17047         
17048         Roo.each(this.picker().select('>div',true).elements, function(v){
17049             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17050             v.hide();
17051         });
17052         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17053     },
17054     
17055     place: function()
17056     {
17057         if(this.isInline) {
17058             return;
17059         }
17060         
17061         this.picker().removeClass(['bottom', 'top']);
17062         
17063         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17064             /*
17065              * place to the top of element!
17066              *
17067              */
17068             
17069             this.picker().addClass('top');
17070             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17071             
17072             return;
17073         }
17074         
17075         this.picker().addClass('bottom');
17076         
17077         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17078     },
17079     
17080     parseDate : function(value)
17081     {
17082         if(!value || value instanceof Date){
17083             return value;
17084         }
17085         var v = Date.parseDate(value, this.format);
17086         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17087             v = Date.parseDate(value, 'Y-m-d');
17088         }
17089         if(!v && this.altFormats){
17090             if(!this.altFormatsArray){
17091                 this.altFormatsArray = this.altFormats.split("|");
17092             }
17093             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17094                 v = Date.parseDate(value, this.altFormatsArray[i]);
17095             }
17096         }
17097         return v;
17098     },
17099     
17100     formatDate : function(date, fmt)
17101     {   
17102         return (!date || !(date instanceof Date)) ?
17103         date : date.dateFormat(fmt || this.format);
17104     },
17105     
17106     onFocus : function()
17107     {
17108         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17109         this.show();
17110     },
17111     
17112     onBlur : function()
17113     {
17114         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17115         
17116         var d = this.inputEl().getValue();
17117         
17118         this.setValue(d);
17119                 
17120         this.hide();
17121     },
17122     
17123     show : function()
17124     {
17125         this.picker().show();
17126         this.update();
17127         this.place();
17128         
17129         this.fireEvent('show', this, this.date);
17130     },
17131     
17132     hide : function()
17133     {
17134         if(this.isInline) {
17135             return;
17136         }
17137         this.picker().hide();
17138         this.viewMode = this.startViewMode;
17139         this.showMode();
17140         
17141         this.fireEvent('hide', this, this.date);
17142         
17143     },
17144     
17145     onMousedown: function(e)
17146     {
17147         e.stopPropagation();
17148         e.preventDefault();
17149     },
17150     
17151     keyup: function(e)
17152     {
17153         Roo.bootstrap.DateField.superclass.keyup.call(this);
17154         this.update();
17155     },
17156
17157     setValue: function(v)
17158     {
17159         if(this.fireEvent('beforeselect', this, v) !== false){
17160             var d = new Date(this.parseDate(v) ).clearTime();
17161         
17162             if(isNaN(d.getTime())){
17163                 this.date = this.viewDate = '';
17164                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17165                 return;
17166             }
17167
17168             v = this.formatDate(d);
17169
17170             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17171
17172             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17173
17174             this.update();
17175
17176             this.fireEvent('select', this, this.date);
17177         }
17178     },
17179     
17180     getValue: function()
17181     {
17182         return this.formatDate(this.date);
17183     },
17184     
17185     fireKey: function(e)
17186     {
17187         if (!this.picker().isVisible()){
17188             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17189                 this.show();
17190             }
17191             return;
17192         }
17193         
17194         var dateChanged = false,
17195         dir, day, month,
17196         newDate, newViewDate;
17197         
17198         switch(e.keyCode){
17199             case 27: // escape
17200                 this.hide();
17201                 e.preventDefault();
17202                 break;
17203             case 37: // left
17204             case 39: // right
17205                 if (!this.keyboardNavigation) {
17206                     break;
17207                 }
17208                 dir = e.keyCode == 37 ? -1 : 1;
17209                 
17210                 if (e.ctrlKey){
17211                     newDate = this.moveYear(this.date, dir);
17212                     newViewDate = this.moveYear(this.viewDate, dir);
17213                 } else if (e.shiftKey){
17214                     newDate = this.moveMonth(this.date, dir);
17215                     newViewDate = this.moveMonth(this.viewDate, dir);
17216                 } else {
17217                     newDate = new Date(this.date);
17218                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17219                     newViewDate = new Date(this.viewDate);
17220                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17221                 }
17222                 if (this.dateWithinRange(newDate)){
17223                     this.date = newDate;
17224                     this.viewDate = newViewDate;
17225                     this.setValue(this.formatDate(this.date));
17226 //                    this.update();
17227                     e.preventDefault();
17228                     dateChanged = true;
17229                 }
17230                 break;
17231             case 38: // up
17232             case 40: // down
17233                 if (!this.keyboardNavigation) {
17234                     break;
17235                 }
17236                 dir = e.keyCode == 38 ? -1 : 1;
17237                 if (e.ctrlKey){
17238                     newDate = this.moveYear(this.date, dir);
17239                     newViewDate = this.moveYear(this.viewDate, dir);
17240                 } else if (e.shiftKey){
17241                     newDate = this.moveMonth(this.date, dir);
17242                     newViewDate = this.moveMonth(this.viewDate, dir);
17243                 } else {
17244                     newDate = new Date(this.date);
17245                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17246                     newViewDate = new Date(this.viewDate);
17247                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17248                 }
17249                 if (this.dateWithinRange(newDate)){
17250                     this.date = newDate;
17251                     this.viewDate = newViewDate;
17252                     this.setValue(this.formatDate(this.date));
17253 //                    this.update();
17254                     e.preventDefault();
17255                     dateChanged = true;
17256                 }
17257                 break;
17258             case 13: // enter
17259                 this.setValue(this.formatDate(this.date));
17260                 this.hide();
17261                 e.preventDefault();
17262                 break;
17263             case 9: // tab
17264                 this.setValue(this.formatDate(this.date));
17265                 this.hide();
17266                 break;
17267             case 16: // shift
17268             case 17: // ctrl
17269             case 18: // alt
17270                 break;
17271             default :
17272                 this.hide();
17273                 
17274         }
17275     },
17276     
17277     
17278     onClick: function(e) 
17279     {
17280         e.stopPropagation();
17281         e.preventDefault();
17282         
17283         var target = e.getTarget();
17284         
17285         if(target.nodeName.toLowerCase() === 'i'){
17286             target = Roo.get(target).dom.parentNode;
17287         }
17288         
17289         var nodeName = target.nodeName;
17290         var className = target.className;
17291         var html = target.innerHTML;
17292         //Roo.log(nodeName);
17293         
17294         switch(nodeName.toLowerCase()) {
17295             case 'th':
17296                 switch(className) {
17297                     case 'switch':
17298                         this.showMode(1);
17299                         break;
17300                     case 'prev':
17301                     case 'next':
17302                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17303                         switch(this.viewMode){
17304                                 case 0:
17305                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17306                                         break;
17307                                 case 1:
17308                                 case 2:
17309                                         this.viewDate = this.moveYear(this.viewDate, dir);
17310                                         break;
17311                         }
17312                         this.fill();
17313                         break;
17314                     case 'today':
17315                         var date = new Date();
17316                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17317 //                        this.fill()
17318                         this.setValue(this.formatDate(this.date));
17319                         
17320                         this.hide();
17321                         break;
17322                 }
17323                 break;
17324             case 'span':
17325                 if (className.indexOf('disabled') < 0) {
17326                     this.viewDate.setUTCDate(1);
17327                     if (className.indexOf('month') > -1) {
17328                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17329                     } else {
17330                         var year = parseInt(html, 10) || 0;
17331                         this.viewDate.setUTCFullYear(year);
17332                         
17333                     }
17334                     
17335                     if(this.singleMode){
17336                         this.setValue(this.formatDate(this.viewDate));
17337                         this.hide();
17338                         return;
17339                     }
17340                     
17341                     this.showMode(-1);
17342                     this.fill();
17343                 }
17344                 break;
17345                 
17346             case 'td':
17347                 //Roo.log(className);
17348                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17349                     var day = parseInt(html, 10) || 1;
17350                     var year = this.viewDate.getUTCFullYear(),
17351                         month = this.viewDate.getUTCMonth();
17352
17353                     if (className.indexOf('old') > -1) {
17354                         if(month === 0 ){
17355                             month = 11;
17356                             year -= 1;
17357                         }else{
17358                             month -= 1;
17359                         }
17360                     } else if (className.indexOf('new') > -1) {
17361                         if (month == 11) {
17362                             month = 0;
17363                             year += 1;
17364                         } else {
17365                             month += 1;
17366                         }
17367                     }
17368                     //Roo.log([year,month,day]);
17369                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17370                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17371 //                    this.fill();
17372                     //Roo.log(this.formatDate(this.date));
17373                     this.setValue(this.formatDate(this.date));
17374                     this.hide();
17375                 }
17376                 break;
17377         }
17378     },
17379     
17380     setStartDate: function(startDate)
17381     {
17382         this.startDate = startDate || -Infinity;
17383         if (this.startDate !== -Infinity) {
17384             this.startDate = this.parseDate(this.startDate);
17385         }
17386         this.update();
17387         this.updateNavArrows();
17388     },
17389
17390     setEndDate: function(endDate)
17391     {
17392         this.endDate = endDate || Infinity;
17393         if (this.endDate !== Infinity) {
17394             this.endDate = this.parseDate(this.endDate);
17395         }
17396         this.update();
17397         this.updateNavArrows();
17398     },
17399     
17400     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17401     {
17402         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17403         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17404             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17405         }
17406         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17407             return parseInt(d, 10);
17408         });
17409         this.update();
17410         this.updateNavArrows();
17411     },
17412     
17413     updateNavArrows: function() 
17414     {
17415         if(this.singleMode){
17416             return;
17417         }
17418         
17419         var d = new Date(this.viewDate),
17420         year = d.getUTCFullYear(),
17421         month = d.getUTCMonth();
17422         
17423         Roo.each(this.picker().select('.prev', true).elements, function(v){
17424             v.show();
17425             switch (this.viewMode) {
17426                 case 0:
17427
17428                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17429                         v.hide();
17430                     }
17431                     break;
17432                 case 1:
17433                 case 2:
17434                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17435                         v.hide();
17436                     }
17437                     break;
17438             }
17439         });
17440         
17441         Roo.each(this.picker().select('.next', true).elements, function(v){
17442             v.show();
17443             switch (this.viewMode) {
17444                 case 0:
17445
17446                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17447                         v.hide();
17448                     }
17449                     break;
17450                 case 1:
17451                 case 2:
17452                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17453                         v.hide();
17454                     }
17455                     break;
17456             }
17457         })
17458     },
17459     
17460     moveMonth: function(date, dir)
17461     {
17462         if (!dir) {
17463             return date;
17464         }
17465         var new_date = new Date(date.valueOf()),
17466         day = new_date.getUTCDate(),
17467         month = new_date.getUTCMonth(),
17468         mag = Math.abs(dir),
17469         new_month, test;
17470         dir = dir > 0 ? 1 : -1;
17471         if (mag == 1){
17472             test = dir == -1
17473             // If going back one month, make sure month is not current month
17474             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17475             ? function(){
17476                 return new_date.getUTCMonth() == month;
17477             }
17478             // If going forward one month, make sure month is as expected
17479             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17480             : function(){
17481                 return new_date.getUTCMonth() != new_month;
17482             };
17483             new_month = month + dir;
17484             new_date.setUTCMonth(new_month);
17485             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17486             if (new_month < 0 || new_month > 11) {
17487                 new_month = (new_month + 12) % 12;
17488             }
17489         } else {
17490             // For magnitudes >1, move one month at a time...
17491             for (var i=0; i<mag; i++) {
17492                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17493                 new_date = this.moveMonth(new_date, dir);
17494             }
17495             // ...then reset the day, keeping it in the new month
17496             new_month = new_date.getUTCMonth();
17497             new_date.setUTCDate(day);
17498             test = function(){
17499                 return new_month != new_date.getUTCMonth();
17500             };
17501         }
17502         // Common date-resetting loop -- if date is beyond end of month, make it
17503         // end of month
17504         while (test()){
17505             new_date.setUTCDate(--day);
17506             new_date.setUTCMonth(new_month);
17507         }
17508         return new_date;
17509     },
17510
17511     moveYear: function(date, dir)
17512     {
17513         return this.moveMonth(date, dir*12);
17514     },
17515
17516     dateWithinRange: function(date)
17517     {
17518         return date >= this.startDate && date <= this.endDate;
17519     },
17520
17521     
17522     remove: function() 
17523     {
17524         this.picker().remove();
17525     }
17526    
17527 });
17528
17529 Roo.apply(Roo.bootstrap.DateField,  {
17530     
17531     head : {
17532         tag: 'thead',
17533         cn: [
17534         {
17535             tag: 'tr',
17536             cn: [
17537             {
17538                 tag: 'th',
17539                 cls: 'prev',
17540                 html: '<i class="fa fa-arrow-left"/>'
17541             },
17542             {
17543                 tag: 'th',
17544                 cls: 'switch',
17545                 colspan: '5'
17546             },
17547             {
17548                 tag: 'th',
17549                 cls: 'next',
17550                 html: '<i class="fa fa-arrow-right"/>'
17551             }
17552
17553             ]
17554         }
17555         ]
17556     },
17557     
17558     content : {
17559         tag: 'tbody',
17560         cn: [
17561         {
17562             tag: 'tr',
17563             cn: [
17564             {
17565                 tag: 'td',
17566                 colspan: '7'
17567             }
17568             ]
17569         }
17570         ]
17571     },
17572     
17573     footer : {
17574         tag: 'tfoot',
17575         cn: [
17576         {
17577             tag: 'tr',
17578             cn: [
17579             {
17580                 tag: 'th',
17581                 colspan: '7',
17582                 cls: 'today'
17583             }
17584                     
17585             ]
17586         }
17587         ]
17588     },
17589     
17590     dates:{
17591         en: {
17592             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17593             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17594             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17595             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17596             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17597             today: "Today"
17598         }
17599     },
17600     
17601     modes: [
17602     {
17603         clsName: 'days',
17604         navFnc: 'Month',
17605         navStep: 1
17606     },
17607     {
17608         clsName: 'months',
17609         navFnc: 'FullYear',
17610         navStep: 1
17611     },
17612     {
17613         clsName: 'years',
17614         navFnc: 'FullYear',
17615         navStep: 10
17616     }]
17617 });
17618
17619 Roo.apply(Roo.bootstrap.DateField,  {
17620   
17621     template : {
17622         tag: 'div',
17623         cls: 'datepicker dropdown-menu roo-dynamic',
17624         cn: [
17625         {
17626             tag: 'div',
17627             cls: 'datepicker-days',
17628             cn: [
17629             {
17630                 tag: 'table',
17631                 cls: 'table-condensed',
17632                 cn:[
17633                 Roo.bootstrap.DateField.head,
17634                 {
17635                     tag: 'tbody'
17636                 },
17637                 Roo.bootstrap.DateField.footer
17638                 ]
17639             }
17640             ]
17641         },
17642         {
17643             tag: 'div',
17644             cls: 'datepicker-months',
17645             cn: [
17646             {
17647                 tag: 'table',
17648                 cls: 'table-condensed',
17649                 cn:[
17650                 Roo.bootstrap.DateField.head,
17651                 Roo.bootstrap.DateField.content,
17652                 Roo.bootstrap.DateField.footer
17653                 ]
17654             }
17655             ]
17656         },
17657         {
17658             tag: 'div',
17659             cls: 'datepicker-years',
17660             cn: [
17661             {
17662                 tag: 'table',
17663                 cls: 'table-condensed',
17664                 cn:[
17665                 Roo.bootstrap.DateField.head,
17666                 Roo.bootstrap.DateField.content,
17667                 Roo.bootstrap.DateField.footer
17668                 ]
17669             }
17670             ]
17671         }
17672         ]
17673     }
17674 });
17675
17676  
17677
17678  /*
17679  * - LGPL
17680  *
17681  * TimeField
17682  * 
17683  */
17684
17685 /**
17686  * @class Roo.bootstrap.TimeField
17687  * @extends Roo.bootstrap.Input
17688  * Bootstrap DateField class
17689  * 
17690  * 
17691  * @constructor
17692  * Create a new TimeField
17693  * @param {Object} config The config object
17694  */
17695
17696 Roo.bootstrap.TimeField = function(config){
17697     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
17698     this.addEvents({
17699             /**
17700              * @event show
17701              * Fires when this field show.
17702              * @param {Roo.bootstrap.DateField} thisthis
17703              * @param {Mixed} date The date value
17704              */
17705             show : true,
17706             /**
17707              * @event show
17708              * Fires when this field hide.
17709              * @param {Roo.bootstrap.DateField} this
17710              * @param {Mixed} date The date value
17711              */
17712             hide : true,
17713             /**
17714              * @event select
17715              * Fires when select a date.
17716              * @param {Roo.bootstrap.DateField} this
17717              * @param {Mixed} date The date value
17718              */
17719             select : true
17720         });
17721 };
17722
17723 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
17724     
17725     /**
17726      * @cfg {String} format
17727      * The default time format string which can be overriden for localization support.  The format must be
17728      * valid according to {@link Date#parseDate} (defaults to 'H:i').
17729      */
17730     format : "H:i",
17731        
17732     onRender: function(ct, position)
17733     {
17734         
17735         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
17736                 
17737         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
17738         
17739         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17740         
17741         this.pop = this.picker().select('>.datepicker-time',true).first();
17742         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17743         
17744         this.picker().on('mousedown', this.onMousedown, this);
17745         this.picker().on('click', this.onClick, this);
17746         
17747         this.picker().addClass('datepicker-dropdown');
17748     
17749         this.fillTime();
17750         this.update();
17751             
17752         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
17753         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
17754         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
17755         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
17756         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
17757         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
17758
17759     },
17760     
17761     fireKey: function(e){
17762         if (!this.picker().isVisible()){
17763             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17764                 this.show();
17765             }
17766             return;
17767         }
17768
17769         e.preventDefault();
17770         
17771         switch(e.keyCode){
17772             case 27: // escape
17773                 this.hide();
17774                 break;
17775             case 37: // left
17776             case 39: // right
17777                 this.onTogglePeriod();
17778                 break;
17779             case 38: // up
17780                 this.onIncrementMinutes();
17781                 break;
17782             case 40: // down
17783                 this.onDecrementMinutes();
17784                 break;
17785             case 13: // enter
17786             case 9: // tab
17787                 this.setTime();
17788                 break;
17789         }
17790     },
17791     
17792     onClick: function(e) {
17793         e.stopPropagation();
17794         e.preventDefault();
17795     },
17796     
17797     picker : function()
17798     {
17799         return this.el.select('.datepicker', true).first();
17800     },
17801     
17802     fillTime: function()
17803     {    
17804         var time = this.pop.select('tbody', true).first();
17805         
17806         time.dom.innerHTML = '';
17807         
17808         time.createChild({
17809             tag: 'tr',
17810             cn: [
17811                 {
17812                     tag: 'td',
17813                     cn: [
17814                         {
17815                             tag: 'a',
17816                             href: '#',
17817                             cls: 'btn',
17818                             cn: [
17819                                 {
17820                                     tag: 'span',
17821                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
17822                                 }
17823                             ]
17824                         } 
17825                     ]
17826                 },
17827                 {
17828                     tag: 'td',
17829                     cls: 'separator'
17830                 },
17831                 {
17832                     tag: 'td',
17833                     cn: [
17834                         {
17835                             tag: 'a',
17836                             href: '#',
17837                             cls: 'btn',
17838                             cn: [
17839                                 {
17840                                     tag: 'span',
17841                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
17842                                 }
17843                             ]
17844                         }
17845                     ]
17846                 },
17847                 {
17848                     tag: 'td',
17849                     cls: 'separator'
17850                 }
17851             ]
17852         });
17853         
17854         time.createChild({
17855             tag: 'tr',
17856             cn: [
17857                 {
17858                     tag: 'td',
17859                     cn: [
17860                         {
17861                             tag: 'span',
17862                             cls: 'timepicker-hour',
17863                             html: '00'
17864                         }  
17865                     ]
17866                 },
17867                 {
17868                     tag: 'td',
17869                     cls: 'separator',
17870                     html: ':'
17871                 },
17872                 {
17873                     tag: 'td',
17874                     cn: [
17875                         {
17876                             tag: 'span',
17877                             cls: 'timepicker-minute',
17878                             html: '00'
17879                         }  
17880                     ]
17881                 },
17882                 {
17883                     tag: 'td',
17884                     cls: 'separator'
17885                 },
17886                 {
17887                     tag: 'td',
17888                     cn: [
17889                         {
17890                             tag: 'button',
17891                             type: 'button',
17892                             cls: 'btn btn-primary period',
17893                             html: 'AM'
17894                             
17895                         }
17896                     ]
17897                 }
17898             ]
17899         });
17900         
17901         time.createChild({
17902             tag: 'tr',
17903             cn: [
17904                 {
17905                     tag: 'td',
17906                     cn: [
17907                         {
17908                             tag: 'a',
17909                             href: '#',
17910                             cls: 'btn',
17911                             cn: [
17912                                 {
17913                                     tag: 'span',
17914                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
17915                                 }
17916                             ]
17917                         }
17918                     ]
17919                 },
17920                 {
17921                     tag: 'td',
17922                     cls: 'separator'
17923                 },
17924                 {
17925                     tag: 'td',
17926                     cn: [
17927                         {
17928                             tag: 'a',
17929                             href: '#',
17930                             cls: 'btn',
17931                             cn: [
17932                                 {
17933                                     tag: 'span',
17934                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
17935                                 }
17936                             ]
17937                         }
17938                     ]
17939                 },
17940                 {
17941                     tag: 'td',
17942                     cls: 'separator'
17943                 }
17944             ]
17945         });
17946         
17947     },
17948     
17949     update: function()
17950     {
17951         
17952         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
17953         
17954         this.fill();
17955     },
17956     
17957     fill: function() 
17958     {
17959         var hours = this.time.getHours();
17960         var minutes = this.time.getMinutes();
17961         var period = 'AM';
17962         
17963         if(hours > 11){
17964             period = 'PM';
17965         }
17966         
17967         if(hours == 0){
17968             hours = 12;
17969         }
17970         
17971         
17972         if(hours > 12){
17973             hours = hours - 12;
17974         }
17975         
17976         if(hours < 10){
17977             hours = '0' + hours;
17978         }
17979         
17980         if(minutes < 10){
17981             minutes = '0' + minutes;
17982         }
17983         
17984         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
17985         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
17986         this.pop.select('button', true).first().dom.innerHTML = period;
17987         
17988     },
17989     
17990     place: function()
17991     {   
17992         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
17993         
17994         var cls = ['bottom'];
17995         
17996         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
17997             cls.pop();
17998             cls.push('top');
17999         }
18000         
18001         cls.push('right');
18002         
18003         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18004             cls.pop();
18005             cls.push('left');
18006         }
18007         
18008         this.picker().addClass(cls.join('-'));
18009         
18010         var _this = this;
18011         
18012         Roo.each(cls, function(c){
18013             if(c == 'bottom'){
18014                 _this.picker().setTop(_this.inputEl().getHeight());
18015                 return;
18016             }
18017             if(c == 'top'){
18018                 _this.picker().setTop(0 - _this.picker().getHeight());
18019                 return;
18020             }
18021             
18022             if(c == 'left'){
18023                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18024                 return;
18025             }
18026             if(c == 'right'){
18027                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18028                 return;
18029             }
18030         });
18031         
18032     },
18033   
18034     onFocus : function()
18035     {
18036         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18037         this.show();
18038     },
18039     
18040     onBlur : function()
18041     {
18042         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18043         this.hide();
18044     },
18045     
18046     show : function()
18047     {
18048         this.picker().show();
18049         this.pop.show();
18050         this.update();
18051         this.place();
18052         
18053         this.fireEvent('show', this, this.date);
18054     },
18055     
18056     hide : function()
18057     {
18058         this.picker().hide();
18059         this.pop.hide();
18060         
18061         this.fireEvent('hide', this, this.date);
18062     },
18063     
18064     setTime : function()
18065     {
18066         this.hide();
18067         this.setValue(this.time.format(this.format));
18068         
18069         this.fireEvent('select', this, this.date);
18070         
18071         
18072     },
18073     
18074     onMousedown: function(e){
18075         e.stopPropagation();
18076         e.preventDefault();
18077     },
18078     
18079     onIncrementHours: function()
18080     {
18081         Roo.log('onIncrementHours');
18082         this.time = this.time.add(Date.HOUR, 1);
18083         this.update();
18084         
18085     },
18086     
18087     onDecrementHours: function()
18088     {
18089         Roo.log('onDecrementHours');
18090         this.time = this.time.add(Date.HOUR, -1);
18091         this.update();
18092     },
18093     
18094     onIncrementMinutes: function()
18095     {
18096         Roo.log('onIncrementMinutes');
18097         this.time = this.time.add(Date.MINUTE, 1);
18098         this.update();
18099     },
18100     
18101     onDecrementMinutes: function()
18102     {
18103         Roo.log('onDecrementMinutes');
18104         this.time = this.time.add(Date.MINUTE, -1);
18105         this.update();
18106     },
18107     
18108     onTogglePeriod: function()
18109     {
18110         Roo.log('onTogglePeriod');
18111         this.time = this.time.add(Date.HOUR, 12);
18112         this.update();
18113     }
18114     
18115    
18116 });
18117
18118 Roo.apply(Roo.bootstrap.TimeField,  {
18119     
18120     content : {
18121         tag: 'tbody',
18122         cn: [
18123             {
18124                 tag: 'tr',
18125                 cn: [
18126                 {
18127                     tag: 'td',
18128                     colspan: '7'
18129                 }
18130                 ]
18131             }
18132         ]
18133     },
18134     
18135     footer : {
18136         tag: 'tfoot',
18137         cn: [
18138             {
18139                 tag: 'tr',
18140                 cn: [
18141                 {
18142                     tag: 'th',
18143                     colspan: '7',
18144                     cls: '',
18145                     cn: [
18146                         {
18147                             tag: 'button',
18148                             cls: 'btn btn-info ok',
18149                             html: 'OK'
18150                         }
18151                     ]
18152                 }
18153
18154                 ]
18155             }
18156         ]
18157     }
18158 });
18159
18160 Roo.apply(Roo.bootstrap.TimeField,  {
18161   
18162     template : {
18163         tag: 'div',
18164         cls: 'datepicker dropdown-menu',
18165         cn: [
18166             {
18167                 tag: 'div',
18168                 cls: 'datepicker-time',
18169                 cn: [
18170                 {
18171                     tag: 'table',
18172                     cls: 'table-condensed',
18173                     cn:[
18174                     Roo.bootstrap.TimeField.content,
18175                     Roo.bootstrap.TimeField.footer
18176                     ]
18177                 }
18178                 ]
18179             }
18180         ]
18181     }
18182 });
18183
18184  
18185
18186  /*
18187  * - LGPL
18188  *
18189  * MonthField
18190  * 
18191  */
18192
18193 /**
18194  * @class Roo.bootstrap.MonthField
18195  * @extends Roo.bootstrap.Input
18196  * Bootstrap MonthField class
18197  * 
18198  * @cfg {String} language default en
18199  * 
18200  * @constructor
18201  * Create a new MonthField
18202  * @param {Object} config The config object
18203  */
18204
18205 Roo.bootstrap.MonthField = function(config){
18206     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18207     
18208     this.addEvents({
18209         /**
18210          * @event show
18211          * Fires when this field show.
18212          * @param {Roo.bootstrap.MonthField} this
18213          * @param {Mixed} date The date value
18214          */
18215         show : true,
18216         /**
18217          * @event show
18218          * Fires when this field hide.
18219          * @param {Roo.bootstrap.MonthField} this
18220          * @param {Mixed} date The date value
18221          */
18222         hide : true,
18223         /**
18224          * @event select
18225          * Fires when select a date.
18226          * @param {Roo.bootstrap.MonthField} this
18227          * @param {String} oldvalue The old value
18228          * @param {String} newvalue The new value
18229          */
18230         select : true
18231     });
18232 };
18233
18234 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18235     
18236     onRender: function(ct, position)
18237     {
18238         
18239         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18240         
18241         this.language = this.language || 'en';
18242         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18243         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18244         
18245         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18246         this.isInline = false;
18247         this.isInput = true;
18248         this.component = this.el.select('.add-on', true).first() || false;
18249         this.component = (this.component && this.component.length === 0) ? false : this.component;
18250         this.hasInput = this.component && this.inputEL().length;
18251         
18252         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18253         
18254         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18255         
18256         this.picker().on('mousedown', this.onMousedown, this);
18257         this.picker().on('click', this.onClick, this);
18258         
18259         this.picker().addClass('datepicker-dropdown');
18260         
18261         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18262             v.setStyle('width', '189px');
18263         });
18264         
18265         this.fillMonths();
18266         
18267         this.update();
18268         
18269         if(this.isInline) {
18270             this.show();
18271         }
18272         
18273     },
18274     
18275     setValue: function(v, suppressEvent)
18276     {   
18277         var o = this.getValue();
18278         
18279         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18280         
18281         this.update();
18282
18283         if(suppressEvent !== true){
18284             this.fireEvent('select', this, o, v);
18285         }
18286         
18287     },
18288     
18289     getValue: function()
18290     {
18291         return this.value;
18292     },
18293     
18294     onClick: function(e) 
18295     {
18296         e.stopPropagation();
18297         e.preventDefault();
18298         
18299         var target = e.getTarget();
18300         
18301         if(target.nodeName.toLowerCase() === 'i'){
18302             target = Roo.get(target).dom.parentNode;
18303         }
18304         
18305         var nodeName = target.nodeName;
18306         var className = target.className;
18307         var html = target.innerHTML;
18308         
18309         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18310             return;
18311         }
18312         
18313         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18314         
18315         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18316         
18317         this.hide();
18318                         
18319     },
18320     
18321     picker : function()
18322     {
18323         return this.pickerEl;
18324     },
18325     
18326     fillMonths: function()
18327     {    
18328         var i = 0;
18329         var months = this.picker().select('>.datepicker-months td', true).first();
18330         
18331         months.dom.innerHTML = '';
18332         
18333         while (i < 12) {
18334             var month = {
18335                 tag: 'span',
18336                 cls: 'month',
18337                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18338             };
18339             
18340             months.createChild(month);
18341         }
18342         
18343     },
18344     
18345     update: function()
18346     {
18347         var _this = this;
18348         
18349         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18350             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18351         }
18352         
18353         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18354             e.removeClass('active');
18355             
18356             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18357                 e.addClass('active');
18358             }
18359         })
18360     },
18361     
18362     place: function()
18363     {
18364         if(this.isInline) {
18365             return;
18366         }
18367         
18368         this.picker().removeClass(['bottom', 'top']);
18369         
18370         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18371             /*
18372              * place to the top of element!
18373              *
18374              */
18375             
18376             this.picker().addClass('top');
18377             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18378             
18379             return;
18380         }
18381         
18382         this.picker().addClass('bottom');
18383         
18384         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18385     },
18386     
18387     onFocus : function()
18388     {
18389         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18390         this.show();
18391     },
18392     
18393     onBlur : function()
18394     {
18395         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18396         
18397         var d = this.inputEl().getValue();
18398         
18399         this.setValue(d);
18400                 
18401         this.hide();
18402     },
18403     
18404     show : function()
18405     {
18406         this.picker().show();
18407         this.picker().select('>.datepicker-months', true).first().show();
18408         this.update();
18409         this.place();
18410         
18411         this.fireEvent('show', this, this.date);
18412     },
18413     
18414     hide : function()
18415     {
18416         if(this.isInline) {
18417             return;
18418         }
18419         this.picker().hide();
18420         this.fireEvent('hide', this, this.date);
18421         
18422     },
18423     
18424     onMousedown: function(e)
18425     {
18426         e.stopPropagation();
18427         e.preventDefault();
18428     },
18429     
18430     keyup: function(e)
18431     {
18432         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18433         this.update();
18434     },
18435
18436     fireKey: function(e)
18437     {
18438         if (!this.picker().isVisible()){
18439             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18440                 this.show();
18441             }
18442             return;
18443         }
18444         
18445         var dir;
18446         
18447         switch(e.keyCode){
18448             case 27: // escape
18449                 this.hide();
18450                 e.preventDefault();
18451                 break;
18452             case 37: // left
18453             case 39: // right
18454                 dir = e.keyCode == 37 ? -1 : 1;
18455                 
18456                 this.vIndex = this.vIndex + dir;
18457                 
18458                 if(this.vIndex < 0){
18459                     this.vIndex = 0;
18460                 }
18461                 
18462                 if(this.vIndex > 11){
18463                     this.vIndex = 11;
18464                 }
18465                 
18466                 if(isNaN(this.vIndex)){
18467                     this.vIndex = 0;
18468                 }
18469                 
18470                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18471                 
18472                 break;
18473             case 38: // up
18474             case 40: // down
18475                 
18476                 dir = e.keyCode == 38 ? -1 : 1;
18477                 
18478                 this.vIndex = this.vIndex + dir * 4;
18479                 
18480                 if(this.vIndex < 0){
18481                     this.vIndex = 0;
18482                 }
18483                 
18484                 if(this.vIndex > 11){
18485                     this.vIndex = 11;
18486                 }
18487                 
18488                 if(isNaN(this.vIndex)){
18489                     this.vIndex = 0;
18490                 }
18491                 
18492                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18493                 break;
18494                 
18495             case 13: // enter
18496                 
18497                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18498                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18499                 }
18500                 
18501                 this.hide();
18502                 e.preventDefault();
18503                 break;
18504             case 9: // tab
18505                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18506                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18507                 }
18508                 this.hide();
18509                 break;
18510             case 16: // shift
18511             case 17: // ctrl
18512             case 18: // alt
18513                 break;
18514             default :
18515                 this.hide();
18516                 
18517         }
18518     },
18519     
18520     remove: function() 
18521     {
18522         this.picker().remove();
18523     }
18524    
18525 });
18526
18527 Roo.apply(Roo.bootstrap.MonthField,  {
18528     
18529     content : {
18530         tag: 'tbody',
18531         cn: [
18532         {
18533             tag: 'tr',
18534             cn: [
18535             {
18536                 tag: 'td',
18537                 colspan: '7'
18538             }
18539             ]
18540         }
18541         ]
18542     },
18543     
18544     dates:{
18545         en: {
18546             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18547             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18548         }
18549     }
18550 });
18551
18552 Roo.apply(Roo.bootstrap.MonthField,  {
18553   
18554     template : {
18555         tag: 'div',
18556         cls: 'datepicker dropdown-menu roo-dynamic',
18557         cn: [
18558             {
18559                 tag: 'div',
18560                 cls: 'datepicker-months',
18561                 cn: [
18562                 {
18563                     tag: 'table',
18564                     cls: 'table-condensed',
18565                     cn:[
18566                         Roo.bootstrap.DateField.content
18567                     ]
18568                 }
18569                 ]
18570             }
18571         ]
18572     }
18573 });
18574
18575  
18576
18577  
18578  /*
18579  * - LGPL
18580  *
18581  * CheckBox
18582  * 
18583  */
18584
18585 /**
18586  * @class Roo.bootstrap.CheckBox
18587  * @extends Roo.bootstrap.Input
18588  * Bootstrap CheckBox class
18589  * 
18590  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18591  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18592  * @cfg {String} boxLabel The text that appears beside the checkbox
18593  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18594  * @cfg {Boolean} checked initnal the element
18595  * @cfg {Boolean} inline inline the element (default false)
18596  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18597  * 
18598  * @constructor
18599  * Create a new CheckBox
18600  * @param {Object} config The config object
18601  */
18602
18603 Roo.bootstrap.CheckBox = function(config){
18604     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18605    
18606     this.addEvents({
18607         /**
18608         * @event check
18609         * Fires when the element is checked or unchecked.
18610         * @param {Roo.bootstrap.CheckBox} this This input
18611         * @param {Boolean} checked The new checked value
18612         */
18613        check : true
18614     });
18615     
18616 };
18617
18618 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18619   
18620     inputType: 'checkbox',
18621     inputValue: 1,
18622     valueOff: 0,
18623     boxLabel: false,
18624     checked: false,
18625     weight : false,
18626     inline: false,
18627     
18628     getAutoCreate : function()
18629     {
18630         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18631         
18632         var id = Roo.id();
18633         
18634         var cfg = {};
18635         
18636         cfg.cls = 'form-group ' + this.inputType; //input-group
18637         
18638         if(this.inline){
18639             cfg.cls += ' ' + this.inputType + '-inline';
18640         }
18641         
18642         var input =  {
18643             tag: 'input',
18644             id : id,
18645             type : this.inputType,
18646             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18647             cls : 'roo-' + this.inputType, //'form-box',
18648             placeholder : this.placeholder || ''
18649             
18650         };
18651         
18652         if (this.weight) { // Validity check?
18653             cfg.cls += " " + this.inputType + "-" + this.weight;
18654         }
18655         
18656         if (this.disabled) {
18657             input.disabled=true;
18658         }
18659         
18660         if(this.checked){
18661             input.checked = this.checked;
18662         }
18663         
18664         if (this.name) {
18665             input.name = this.name;
18666         }
18667         
18668         if (this.size) {
18669             input.cls += ' input-' + this.size;
18670         }
18671         
18672         var settings=this;
18673         
18674         ['xs','sm','md','lg'].map(function(size){
18675             if (settings[size]) {
18676                 cfg.cls += ' col-' + size + '-' + settings[size];
18677             }
18678         });
18679         
18680         var inputblock = input;
18681          
18682         if (this.before || this.after) {
18683             
18684             inputblock = {
18685                 cls : 'input-group',
18686                 cn :  [] 
18687             };
18688             
18689             if (this.before) {
18690                 inputblock.cn.push({
18691                     tag :'span',
18692                     cls : 'input-group-addon',
18693                     html : this.before
18694                 });
18695             }
18696             
18697             inputblock.cn.push(input);
18698             
18699             if (this.after) {
18700                 inputblock.cn.push({
18701                     tag :'span',
18702                     cls : 'input-group-addon',
18703                     html : this.after
18704                 });
18705             }
18706             
18707         }
18708         
18709         if (align ==='left' && this.fieldLabel.length) {
18710 //                Roo.log("left and has label");
18711                 cfg.cn = [
18712                     
18713                     {
18714                         tag: 'label',
18715                         'for' :  id,
18716                         cls : 'control-label col-md-' + this.labelWidth,
18717                         html : this.fieldLabel
18718                         
18719                     },
18720                     {
18721                         cls : "col-md-" + (12 - this.labelWidth), 
18722                         cn: [
18723                             inputblock
18724                         ]
18725                     }
18726                     
18727                 ];
18728         } else if ( this.fieldLabel.length) {
18729 //                Roo.log(" label");
18730                 cfg.cn = [
18731                    
18732                     {
18733                         tag: this.boxLabel ? 'span' : 'label',
18734                         'for': id,
18735                         cls: 'control-label box-input-label',
18736                         //cls : 'input-group-addon',
18737                         html : this.fieldLabel
18738                         
18739                     },
18740                     
18741                     inputblock
18742                     
18743                 ];
18744
18745         } else {
18746             
18747 //                Roo.log(" no label && no align");
18748                 cfg.cn = [  inputblock ] ;
18749                 
18750                 
18751         }
18752         if(this.boxLabel){
18753              var boxLabelCfg = {
18754                 tag: 'label',
18755                 //'for': id, // box label is handled by onclick - so no for...
18756                 cls: 'box-label',
18757                 html: this.boxLabel
18758             };
18759             
18760             if(this.tooltip){
18761                 boxLabelCfg.tooltip = this.tooltip;
18762             }
18763              
18764             cfg.cn.push(boxLabelCfg);
18765         }
18766         
18767         
18768        
18769         return cfg;
18770         
18771     },
18772     
18773     /**
18774      * return the real input element.
18775      */
18776     inputEl: function ()
18777     {
18778         return this.el.select('input.roo-' + this.inputType,true).first();
18779     },
18780     
18781     labelEl: function()
18782     {
18783         return this.el.select('label.control-label',true).first();
18784     },
18785     /* depricated... */
18786     
18787     label: function()
18788     {
18789         return this.labelEl();
18790     },
18791     
18792     initEvents : function()
18793     {
18794 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
18795         
18796         this.inputEl().on('click', this.onClick,  this);
18797         
18798         if (this.boxLabel) { 
18799             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
18800         }
18801         
18802         this.startValue = this.getValue();
18803         
18804         if(this.groupId){
18805             Roo.bootstrap.CheckBox.register(this);
18806         }
18807     },
18808     
18809     onClick : function()
18810     {   
18811         this.setChecked(!this.checked);
18812     },
18813     
18814     setChecked : function(state,suppressEvent)
18815     {
18816         this.startValue = this.getValue();
18817         
18818         if(this.inputType == 'radio'){
18819             
18820             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18821                 e.dom.checked = false;
18822             });
18823             
18824             this.inputEl().dom.checked = true;
18825             
18826             this.inputEl().dom.value = this.inputValue;
18827             
18828             if(suppressEvent !== true){
18829                 this.fireEvent('check', this, true);
18830             }
18831             
18832             this.validate();
18833             
18834             return;
18835         }
18836         
18837         this.checked = state;
18838         
18839         this.inputEl().dom.checked = state;
18840         
18841         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
18842         
18843         if(suppressEvent !== true){
18844             this.fireEvent('check', this, state);
18845         }
18846         
18847         this.validate();
18848     },
18849     
18850     getValue : function()
18851     {
18852         if(this.inputType == 'radio'){
18853             return this.getGroupValue();
18854         }
18855         
18856         return this.inputEl().getValue();
18857         
18858     },
18859     
18860     getGroupValue : function()
18861     {
18862         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
18863             return '';
18864         }
18865         
18866         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
18867     },
18868     
18869     setValue : function(v,suppressEvent)
18870     {
18871         if(this.inputType == 'radio'){
18872             this.setGroupValue(v, suppressEvent);
18873             return;
18874         }
18875         
18876         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
18877         
18878         this.validate();
18879     },
18880     
18881     setGroupValue : function(v, suppressEvent)
18882     {
18883         this.startValue = this.getValue();
18884         
18885         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18886             e.dom.checked = false;
18887             
18888             if(e.dom.value == v){
18889                 e.dom.checked = true;
18890             }
18891         });
18892         
18893         if(suppressEvent !== true){
18894             this.fireEvent('check', this, true);
18895         }
18896
18897         this.validate();
18898         
18899         return;
18900     },
18901     
18902     validate : function()
18903     {
18904         if(
18905                 this.disabled || 
18906                 (this.inputType == 'radio' && this.validateRadio()) ||
18907                 (this.inputType == 'checkbox' && this.validateCheckbox())
18908         ){
18909             this.markValid();
18910             return true;
18911         }
18912         
18913         this.markInvalid();
18914         return false;
18915     },
18916     
18917     validateRadio : function()
18918     {
18919         var valid = false;
18920         
18921         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18922             if(!e.dom.checked){
18923                 return;
18924             }
18925             
18926             valid = true;
18927             
18928             return false;
18929         });
18930         
18931         return valid;
18932     },
18933     
18934     validateCheckbox : function()
18935     {
18936         if(!this.groupId){
18937             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
18938         }
18939         
18940         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18941         
18942         if(!group){
18943             return false;
18944         }
18945         
18946         var r = false;
18947         
18948         for(var i in group){
18949             if(r){
18950                 break;
18951             }
18952             
18953             r = (group[i].getValue() == group[i].inputValue) ? true : false;
18954         }
18955         
18956         return r;
18957     },
18958     
18959     /**
18960      * Mark this field as valid
18961      */
18962     markValid : function()
18963     {
18964         if(this.allowBlank){
18965             return;
18966         }
18967         
18968         var _this = this;
18969         
18970         this.fireEvent('valid', this);
18971         
18972         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
18973         
18974         if(this.groupId){
18975             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
18976         }
18977         
18978         if(label){
18979             label.markValid();
18980         }
18981         
18982         if(this.inputType == 'radio'){
18983             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
18984                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
18985                 e.findParent('.form-group', false, true).addClass(_this.validClass);
18986             });
18987             
18988             return;
18989         }
18990         
18991         if(!this.groupId){
18992             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
18993             this.el.findParent('.form-group', false, true).addClass(this.validClass);
18994             return;
18995         }
18996         
18997         var group = Roo.bootstrap.CheckBox.get(this.groupId);
18998             
18999         if(!group){
19000             return;
19001         }
19002         
19003         for(var i in group){
19004             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19005             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19006         }
19007     },
19008     
19009      /**
19010      * Mark this field as invalid
19011      * @param {String} msg The validation message
19012      */
19013     markInvalid : function(msg)
19014     {
19015         if(this.allowBlank){
19016             return;
19017         }
19018         
19019         var _this = this;
19020         
19021         this.fireEvent('invalid', this, msg);
19022         
19023         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19024         
19025         if(this.groupId){
19026             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19027         }
19028         
19029         if(label){
19030             label.markInvalid();
19031         }
19032             
19033         if(this.inputType == 'radio'){
19034             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19035                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19036                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19037             });
19038             
19039             return;
19040         }
19041         
19042         if(!this.groupId){
19043             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19044             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19045             return;
19046         }
19047         
19048         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19049         
19050         if(!group){
19051             return;
19052         }
19053         
19054         for(var i in group){
19055             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19056             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19057         }
19058         
19059     }
19060     
19061 });
19062
19063 Roo.apply(Roo.bootstrap.CheckBox, {
19064     
19065     groups: {},
19066     
19067      /**
19068     * register a CheckBox Group
19069     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19070     */
19071     register : function(checkbox)
19072     {
19073         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19074             this.groups[checkbox.groupId] = {};
19075         }
19076         
19077         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19078             return;
19079         }
19080         
19081         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19082         
19083     },
19084     /**
19085     * fetch a CheckBox Group based on the group ID
19086     * @param {string} the group ID
19087     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19088     */
19089     get: function(groupId) {
19090         if (typeof(this.groups[groupId]) == 'undefined') {
19091             return false;
19092         }
19093         
19094         return this.groups[groupId] ;
19095     }
19096     
19097     
19098 });
19099 /*
19100  * - LGPL
19101  *
19102  * Radio
19103  *
19104  *
19105  * not inline
19106  *<div class="radio">
19107   <label>
19108     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19109     Option one is this and that&mdash;be sure to include why it's great
19110   </label>
19111 </div>
19112  *
19113  *
19114  *inline
19115  *<span>
19116  *<label class="radio-inline">fieldLabel</label>
19117  *<label class="radio-inline">
19118   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19119 </label>
19120 <span>
19121  * 
19122  * 
19123  */
19124
19125 /**
19126  * @class Roo.bootstrap.Radio
19127  * @extends Roo.bootstrap.CheckBox
19128  * Bootstrap Radio class
19129
19130  * @constructor
19131  * Create a new Radio
19132  * @param {Object} config The config object
19133  */
19134
19135 Roo.bootstrap.Radio = function(config){
19136     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19137    
19138 };
19139
19140 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19141     
19142     inputType: 'radio',
19143     inputValue: '',
19144     valueOff: '',
19145     
19146     getAutoCreate : function()
19147     {
19148         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19149         align = align || 'left'; // default...
19150         
19151         
19152         
19153         var id = Roo.id();
19154         
19155         var cfg = {
19156                 tag : this.inline ? 'span' : 'div',
19157                 cls : '',
19158                 cn : []
19159         };
19160         
19161         var inline = this.inline ? ' radio-inline' : '';
19162         
19163         var lbl = {
19164                 tag: 'label' ,
19165                 // does not need for, as we wrap the input with it..
19166                 'for' : id,
19167                 cls : 'control-label box-label' + inline,
19168                 cn : []
19169         };
19170         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19171         
19172         var fieldLabel = {
19173             tag: 'label' ,
19174             //cls : 'control-label' + inline,
19175             html : this.fieldLabel,
19176             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19177         };
19178         
19179  
19180         
19181         
19182         var input =  {
19183             tag: 'input',
19184             id : id,
19185             type : this.inputType,
19186             //value : (!this.checked) ? this.valueOff : this.inputValue,
19187             value : this.inputValue,
19188             cls : 'roo-radio',
19189             placeholder : this.placeholder || '' // ?? needed????
19190             
19191         };
19192         if (this.weight) { // Validity check?
19193             input.cls += " radio-" + this.weight;
19194         }
19195         if (this.disabled) {
19196             input.disabled=true;
19197         }
19198         
19199         if(this.checked){
19200             input.checked = this.checked;
19201         }
19202         
19203         if (this.name) {
19204             input.name = this.name;
19205         }
19206         
19207         if (this.size) {
19208             input.cls += ' input-' + this.size;
19209         }
19210         
19211         //?? can span's inline have a width??
19212         
19213         var settings=this;
19214         ['xs','sm','md','lg'].map(function(size){
19215             if (settings[size]) {
19216                 cfg.cls += ' col-' + size + '-' + settings[size];
19217             }
19218         });
19219         
19220         var inputblock = input;
19221         
19222         if (this.before || this.after) {
19223             
19224             inputblock = {
19225                 cls : 'input-group',
19226                 tag : 'span',
19227                 cn :  [] 
19228             };
19229             if (this.before) {
19230                 inputblock.cn.push({
19231                     tag :'span',
19232                     cls : 'input-group-addon',
19233                     html : this.before
19234                 });
19235             }
19236             inputblock.cn.push(input);
19237             if (this.after) {
19238                 inputblock.cn.push({
19239                     tag :'span',
19240                     cls : 'input-group-addon',
19241                     html : this.after
19242                 });
19243             }
19244             
19245         };
19246         
19247         
19248         if (this.fieldLabel && this.fieldLabel.length) {
19249             cfg.cn.push(fieldLabel);
19250         }
19251        
19252         // normal bootstrap puts the input inside the label.
19253         // however with our styled version - it has to go after the input.
19254        
19255         //lbl.cn.push(inputblock);
19256         
19257         var lblwrap =  {
19258             tag: 'span',
19259             cls: 'radio' + inline,
19260             cn: [
19261                 inputblock,
19262                 lbl
19263             ]
19264         };
19265         
19266         cfg.cn.push( lblwrap);
19267         
19268         if(this.boxLabel){
19269             lbl.cn.push({
19270                 tag: 'span',
19271                 html: this.boxLabel
19272             })
19273         }
19274          
19275         
19276         return cfg;
19277         
19278     },
19279     
19280     initEvents : function()
19281     {
19282 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19283         
19284         this.inputEl().on('click', this.onClick,  this);
19285         if (this.boxLabel) {
19286             //Roo.log('find label');
19287             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19288         }
19289         
19290     },
19291     
19292     inputEl: function ()
19293     {
19294         return this.el.select('input.roo-radio',true).first();
19295     },
19296     onClick : function()
19297     {   
19298         Roo.log("click");
19299         this.setChecked(true);
19300     },
19301     
19302     setChecked : function(state,suppressEvent)
19303     {
19304         if(state){
19305             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19306                 v.dom.checked = false;
19307             });
19308         }
19309         Roo.log(this.inputEl().dom);
19310         this.checked = state;
19311         this.inputEl().dom.checked = state;
19312         
19313         if(suppressEvent !== true){
19314             this.fireEvent('check', this, state);
19315         }
19316         
19317         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19318         
19319     },
19320     
19321     getGroupValue : function()
19322     {
19323         var value = '';
19324         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19325             if(v.dom.checked == true){
19326                 value = v.dom.value;
19327             }
19328         });
19329         
19330         return value;
19331     },
19332     
19333     /**
19334      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19335      * @return {Mixed} value The field value
19336      */
19337     getValue : function(){
19338         return this.getGroupValue();
19339     }
19340     
19341 });
19342
19343  
19344 //<script type="text/javascript">
19345
19346 /*
19347  * Based  Ext JS Library 1.1.1
19348  * Copyright(c) 2006-2007, Ext JS, LLC.
19349  * LGPL
19350  *
19351  */
19352  
19353 /**
19354  * @class Roo.HtmlEditorCore
19355  * @extends Roo.Component
19356  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19357  *
19358  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19359  */
19360
19361 Roo.HtmlEditorCore = function(config){
19362     
19363     
19364     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19365     
19366     
19367     this.addEvents({
19368         /**
19369          * @event initialize
19370          * Fires when the editor is fully initialized (including the iframe)
19371          * @param {Roo.HtmlEditorCore} this
19372          */
19373         initialize: true,
19374         /**
19375          * @event activate
19376          * Fires when the editor is first receives the focus. Any insertion must wait
19377          * until after this event.
19378          * @param {Roo.HtmlEditorCore} this
19379          */
19380         activate: true,
19381          /**
19382          * @event beforesync
19383          * Fires before the textarea is updated with content from the editor iframe. Return false
19384          * to cancel the sync.
19385          * @param {Roo.HtmlEditorCore} this
19386          * @param {String} html
19387          */
19388         beforesync: true,
19389          /**
19390          * @event beforepush
19391          * Fires before the iframe editor is updated with content from the textarea. Return false
19392          * to cancel the push.
19393          * @param {Roo.HtmlEditorCore} this
19394          * @param {String} html
19395          */
19396         beforepush: true,
19397          /**
19398          * @event sync
19399          * Fires when the textarea is updated with content from the editor iframe.
19400          * @param {Roo.HtmlEditorCore} this
19401          * @param {String} html
19402          */
19403         sync: true,
19404          /**
19405          * @event push
19406          * Fires when the iframe editor is updated with content from the textarea.
19407          * @param {Roo.HtmlEditorCore} this
19408          * @param {String} html
19409          */
19410         push: true,
19411         
19412         /**
19413          * @event editorevent
19414          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19415          * @param {Roo.HtmlEditorCore} this
19416          */
19417         editorevent: true
19418         
19419     });
19420     
19421     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19422     
19423     // defaults : white / black...
19424     this.applyBlacklists();
19425     
19426     
19427     
19428 };
19429
19430
19431 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19432
19433
19434      /**
19435      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19436      */
19437     
19438     owner : false,
19439     
19440      /**
19441      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19442      *                        Roo.resizable.
19443      */
19444     resizable : false,
19445      /**
19446      * @cfg {Number} height (in pixels)
19447      */   
19448     height: 300,
19449    /**
19450      * @cfg {Number} width (in pixels)
19451      */   
19452     width: 500,
19453     
19454     /**
19455      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19456      * 
19457      */
19458     stylesheets: false,
19459     
19460     // id of frame..
19461     frameId: false,
19462     
19463     // private properties
19464     validationEvent : false,
19465     deferHeight: true,
19466     initialized : false,
19467     activated : false,
19468     sourceEditMode : false,
19469     onFocus : Roo.emptyFn,
19470     iframePad:3,
19471     hideMode:'offsets',
19472     
19473     clearUp: true,
19474     
19475     // blacklist + whitelisted elements..
19476     black: false,
19477     white: false,
19478      
19479     
19480
19481     /**
19482      * Protected method that will not generally be called directly. It
19483      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19484      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19485      */
19486     getDocMarkup : function(){
19487         // body styles..
19488         var st = '';
19489         
19490         // inherit styels from page...?? 
19491         if (this.stylesheets === false) {
19492             
19493             Roo.get(document.head).select('style').each(function(node) {
19494                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19495             });
19496             
19497             Roo.get(document.head).select('link').each(function(node) { 
19498                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19499             });
19500             
19501         } else if (!this.stylesheets.length) {
19502                 // simple..
19503                 st = '<style type="text/css">' +
19504                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19505                    '</style>';
19506         } else { 
19507             
19508         }
19509         
19510         st +=  '<style type="text/css">' +
19511             'IMG { cursor: pointer } ' +
19512         '</style>';
19513
19514         
19515         return '<html><head>' + st  +
19516             //<style type="text/css">' +
19517             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19518             //'</style>' +
19519             ' </head><body class="roo-htmleditor-body"></body></html>';
19520     },
19521
19522     // private
19523     onRender : function(ct, position)
19524     {
19525         var _t = this;
19526         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19527         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19528         
19529         
19530         this.el.dom.style.border = '0 none';
19531         this.el.dom.setAttribute('tabIndex', -1);
19532         this.el.addClass('x-hidden hide');
19533         
19534         
19535         
19536         if(Roo.isIE){ // fix IE 1px bogus margin
19537             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19538         }
19539        
19540         
19541         this.frameId = Roo.id();
19542         
19543          
19544         
19545         var iframe = this.owner.wrap.createChild({
19546             tag: 'iframe',
19547             cls: 'form-control', // bootstrap..
19548             id: this.frameId,
19549             name: this.frameId,
19550             frameBorder : 'no',
19551             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19552         }, this.el
19553         );
19554         
19555         
19556         this.iframe = iframe.dom;
19557
19558          this.assignDocWin();
19559         
19560         this.doc.designMode = 'on';
19561        
19562         this.doc.open();
19563         this.doc.write(this.getDocMarkup());
19564         this.doc.close();
19565
19566         
19567         var task = { // must defer to wait for browser to be ready
19568             run : function(){
19569                 //console.log("run task?" + this.doc.readyState);
19570                 this.assignDocWin();
19571                 if(this.doc.body || this.doc.readyState == 'complete'){
19572                     try {
19573                         this.doc.designMode="on";
19574                     } catch (e) {
19575                         return;
19576                     }
19577                     Roo.TaskMgr.stop(task);
19578                     this.initEditor.defer(10, this);
19579                 }
19580             },
19581             interval : 10,
19582             duration: 10000,
19583             scope: this
19584         };
19585         Roo.TaskMgr.start(task);
19586
19587     },
19588
19589     // private
19590     onResize : function(w, h)
19591     {
19592          Roo.log('resize: ' +w + ',' + h );
19593         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19594         if(!this.iframe){
19595             return;
19596         }
19597         if(typeof w == 'number'){
19598             
19599             this.iframe.style.width = w + 'px';
19600         }
19601         if(typeof h == 'number'){
19602             
19603             this.iframe.style.height = h + 'px';
19604             if(this.doc){
19605                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19606             }
19607         }
19608         
19609     },
19610
19611     /**
19612      * Toggles the editor between standard and source edit mode.
19613      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19614      */
19615     toggleSourceEdit : function(sourceEditMode){
19616         
19617         this.sourceEditMode = sourceEditMode === true;
19618         
19619         if(this.sourceEditMode){
19620  
19621             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19622             
19623         }else{
19624             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19625             //this.iframe.className = '';
19626             this.deferFocus();
19627         }
19628         //this.setSize(this.owner.wrap.getSize());
19629         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19630     },
19631
19632     
19633   
19634
19635     /**
19636      * Protected method that will not generally be called directly. If you need/want
19637      * custom HTML cleanup, this is the method you should override.
19638      * @param {String} html The HTML to be cleaned
19639      * return {String} The cleaned HTML
19640      */
19641     cleanHtml : function(html){
19642         html = String(html);
19643         if(html.length > 5){
19644             if(Roo.isSafari){ // strip safari nonsense
19645                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19646             }
19647         }
19648         if(html == '&nbsp;'){
19649             html = '';
19650         }
19651         return html;
19652     },
19653
19654     /**
19655      * HTML Editor -> Textarea
19656      * Protected method that will not generally be called directly. Syncs the contents
19657      * of the editor iframe with the textarea.
19658      */
19659     syncValue : function(){
19660         if(this.initialized){
19661             var bd = (this.doc.body || this.doc.documentElement);
19662             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19663             var html = bd.innerHTML;
19664             if(Roo.isSafari){
19665                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19666                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19667                 if(m && m[1]){
19668                     html = '<div style="'+m[0]+'">' + html + '</div>';
19669                 }
19670             }
19671             html = this.cleanHtml(html);
19672             // fix up the special chars.. normaly like back quotes in word...
19673             // however we do not want to do this with chinese..
19674             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
19675                 var cc = b.charCodeAt();
19676                 if (
19677                     (cc >= 0x4E00 && cc < 0xA000 ) ||
19678                     (cc >= 0x3400 && cc < 0x4E00 ) ||
19679                     (cc >= 0xf900 && cc < 0xfb00 )
19680                 ) {
19681                         return b;
19682                 }
19683                 return "&#"+cc+";" 
19684             });
19685             if(this.owner.fireEvent('beforesync', this, html) !== false){
19686                 this.el.dom.value = html;
19687                 this.owner.fireEvent('sync', this, html);
19688             }
19689         }
19690     },
19691
19692     /**
19693      * Protected method that will not generally be called directly. Pushes the value of the textarea
19694      * into the iframe editor.
19695      */
19696     pushValue : function(){
19697         if(this.initialized){
19698             var v = this.el.dom.value.trim();
19699             
19700 //            if(v.length < 1){
19701 //                v = '&#160;';
19702 //            }
19703             
19704             if(this.owner.fireEvent('beforepush', this, v) !== false){
19705                 var d = (this.doc.body || this.doc.documentElement);
19706                 d.innerHTML = v;
19707                 this.cleanUpPaste();
19708                 this.el.dom.value = d.innerHTML;
19709                 this.owner.fireEvent('push', this, v);
19710             }
19711         }
19712     },
19713
19714     // private
19715     deferFocus : function(){
19716         this.focus.defer(10, this);
19717     },
19718
19719     // doc'ed in Field
19720     focus : function(){
19721         if(this.win && !this.sourceEditMode){
19722             this.win.focus();
19723         }else{
19724             this.el.focus();
19725         }
19726     },
19727     
19728     assignDocWin: function()
19729     {
19730         var iframe = this.iframe;
19731         
19732          if(Roo.isIE){
19733             this.doc = iframe.contentWindow.document;
19734             this.win = iframe.contentWindow;
19735         } else {
19736 //            if (!Roo.get(this.frameId)) {
19737 //                return;
19738 //            }
19739 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19740 //            this.win = Roo.get(this.frameId).dom.contentWindow;
19741             
19742             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
19743                 return;
19744             }
19745             
19746             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
19747             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
19748         }
19749     },
19750     
19751     // private
19752     initEditor : function(){
19753         //console.log("INIT EDITOR");
19754         this.assignDocWin();
19755         
19756         
19757         
19758         this.doc.designMode="on";
19759         this.doc.open();
19760         this.doc.write(this.getDocMarkup());
19761         this.doc.close();
19762         
19763         var dbody = (this.doc.body || this.doc.documentElement);
19764         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
19765         // this copies styles from the containing element into thsi one..
19766         // not sure why we need all of this..
19767         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
19768         
19769         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
19770         //ss['background-attachment'] = 'fixed'; // w3c
19771         dbody.bgProperties = 'fixed'; // ie
19772         //Roo.DomHelper.applyStyles(dbody, ss);
19773         Roo.EventManager.on(this.doc, {
19774             //'mousedown': this.onEditorEvent,
19775             'mouseup': this.onEditorEvent,
19776             'dblclick': this.onEditorEvent,
19777             'click': this.onEditorEvent,
19778             'keyup': this.onEditorEvent,
19779             buffer:100,
19780             scope: this
19781         });
19782         if(Roo.isGecko){
19783             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
19784         }
19785         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
19786             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
19787         }
19788         this.initialized = true;
19789
19790         this.owner.fireEvent('initialize', this);
19791         this.pushValue();
19792     },
19793
19794     // private
19795     onDestroy : function(){
19796         
19797         
19798         
19799         if(this.rendered){
19800             
19801             //for (var i =0; i < this.toolbars.length;i++) {
19802             //    // fixme - ask toolbars for heights?
19803             //    this.toolbars[i].onDestroy();
19804            // }
19805             
19806             //this.wrap.dom.innerHTML = '';
19807             //this.wrap.remove();
19808         }
19809     },
19810
19811     // private
19812     onFirstFocus : function(){
19813         
19814         this.assignDocWin();
19815         
19816         
19817         this.activated = true;
19818          
19819     
19820         if(Roo.isGecko){ // prevent silly gecko errors
19821             this.win.focus();
19822             var s = this.win.getSelection();
19823             if(!s.focusNode || s.focusNode.nodeType != 3){
19824                 var r = s.getRangeAt(0);
19825                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
19826                 r.collapse(true);
19827                 this.deferFocus();
19828             }
19829             try{
19830                 this.execCmd('useCSS', true);
19831                 this.execCmd('styleWithCSS', false);
19832             }catch(e){}
19833         }
19834         this.owner.fireEvent('activate', this);
19835     },
19836
19837     // private
19838     adjustFont: function(btn){
19839         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
19840         //if(Roo.isSafari){ // safari
19841         //    adjust *= 2;
19842        // }
19843         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
19844         if(Roo.isSafari){ // safari
19845             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
19846             v =  (v < 10) ? 10 : v;
19847             v =  (v > 48) ? 48 : v;
19848             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
19849             
19850         }
19851         
19852         
19853         v = Math.max(1, v+adjust);
19854         
19855         this.execCmd('FontSize', v  );
19856     },
19857
19858     onEditorEvent : function(e)
19859     {
19860         this.owner.fireEvent('editorevent', this, e);
19861       //  this.updateToolbar();
19862         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
19863     },
19864
19865     insertTag : function(tg)
19866     {
19867         // could be a bit smarter... -> wrap the current selected tRoo..
19868         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
19869             
19870             range = this.createRange(this.getSelection());
19871             var wrappingNode = this.doc.createElement(tg.toLowerCase());
19872             wrappingNode.appendChild(range.extractContents());
19873             range.insertNode(wrappingNode);
19874
19875             return;
19876             
19877             
19878             
19879         }
19880         this.execCmd("formatblock",   tg);
19881         
19882     },
19883     
19884     insertText : function(txt)
19885     {
19886         
19887         
19888         var range = this.createRange();
19889         range.deleteContents();
19890                //alert(Sender.getAttribute('label'));
19891                
19892         range.insertNode(this.doc.createTextNode(txt));
19893     } ,
19894     
19895      
19896
19897     /**
19898      * Executes a Midas editor command on the editor document and performs necessary focus and
19899      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
19900      * @param {String} cmd The Midas command
19901      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19902      */
19903     relayCmd : function(cmd, value){
19904         this.win.focus();
19905         this.execCmd(cmd, value);
19906         this.owner.fireEvent('editorevent', this);
19907         //this.updateToolbar();
19908         this.owner.deferFocus();
19909     },
19910
19911     /**
19912      * Executes a Midas editor command directly on the editor document.
19913      * For visual commands, you should use {@link #relayCmd} instead.
19914      * <b>This should only be called after the editor is initialized.</b>
19915      * @param {String} cmd The Midas command
19916      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
19917      */
19918     execCmd : function(cmd, value){
19919         this.doc.execCommand(cmd, false, value === undefined ? null : value);
19920         this.syncValue();
19921     },
19922  
19923  
19924    
19925     /**
19926      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
19927      * to insert tRoo.
19928      * @param {String} text | dom node.. 
19929      */
19930     insertAtCursor : function(text)
19931     {
19932         
19933         
19934         
19935         if(!this.activated){
19936             return;
19937         }
19938         /*
19939         if(Roo.isIE){
19940             this.win.focus();
19941             var r = this.doc.selection.createRange();
19942             if(r){
19943                 r.collapse(true);
19944                 r.pasteHTML(text);
19945                 this.syncValue();
19946                 this.deferFocus();
19947             
19948             }
19949             return;
19950         }
19951         */
19952         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
19953             this.win.focus();
19954             
19955             
19956             // from jquery ui (MIT licenced)
19957             var range, node;
19958             var win = this.win;
19959             
19960             if (win.getSelection && win.getSelection().getRangeAt) {
19961                 range = win.getSelection().getRangeAt(0);
19962                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
19963                 range.insertNode(node);
19964             } else if (win.document.selection && win.document.selection.createRange) {
19965                 // no firefox support
19966                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19967                 win.document.selection.createRange().pasteHTML(txt);
19968             } else {
19969                 // no firefox support
19970                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
19971                 this.execCmd('InsertHTML', txt);
19972             } 
19973             
19974             this.syncValue();
19975             
19976             this.deferFocus();
19977         }
19978     },
19979  // private
19980     mozKeyPress : function(e){
19981         if(e.ctrlKey){
19982             var c = e.getCharCode(), cmd;
19983           
19984             if(c > 0){
19985                 c = String.fromCharCode(c).toLowerCase();
19986                 switch(c){
19987                     case 'b':
19988                         cmd = 'bold';
19989                         break;
19990                     case 'i':
19991                         cmd = 'italic';
19992                         break;
19993                     
19994                     case 'u':
19995                         cmd = 'underline';
19996                         break;
19997                     
19998                     case 'v':
19999                         this.cleanUpPaste.defer(100, this);
20000                         return;
20001                         
20002                 }
20003                 if(cmd){
20004                     this.win.focus();
20005                     this.execCmd(cmd);
20006                     this.deferFocus();
20007                     e.preventDefault();
20008                 }
20009                 
20010             }
20011         }
20012     },
20013
20014     // private
20015     fixKeys : function(){ // load time branching for fastest keydown performance
20016         if(Roo.isIE){
20017             return function(e){
20018                 var k = e.getKey(), r;
20019                 if(k == e.TAB){
20020                     e.stopEvent();
20021                     r = this.doc.selection.createRange();
20022                     if(r){
20023                         r.collapse(true);
20024                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20025                         this.deferFocus();
20026                     }
20027                     return;
20028                 }
20029                 
20030                 if(k == e.ENTER){
20031                     r = this.doc.selection.createRange();
20032                     if(r){
20033                         var target = r.parentElement();
20034                         if(!target || target.tagName.toLowerCase() != 'li'){
20035                             e.stopEvent();
20036                             r.pasteHTML('<br />');
20037                             r.collapse(false);
20038                             r.select();
20039                         }
20040                     }
20041                 }
20042                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20043                     this.cleanUpPaste.defer(100, this);
20044                     return;
20045                 }
20046                 
20047                 
20048             };
20049         }else if(Roo.isOpera){
20050             return function(e){
20051                 var k = e.getKey();
20052                 if(k == e.TAB){
20053                     e.stopEvent();
20054                     this.win.focus();
20055                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20056                     this.deferFocus();
20057                 }
20058                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20059                     this.cleanUpPaste.defer(100, this);
20060                     return;
20061                 }
20062                 
20063             };
20064         }else if(Roo.isSafari){
20065             return function(e){
20066                 var k = e.getKey();
20067                 
20068                 if(k == e.TAB){
20069                     e.stopEvent();
20070                     this.execCmd('InsertText','\t');
20071                     this.deferFocus();
20072                     return;
20073                 }
20074                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20075                     this.cleanUpPaste.defer(100, this);
20076                     return;
20077                 }
20078                 
20079              };
20080         }
20081     }(),
20082     
20083     getAllAncestors: function()
20084     {
20085         var p = this.getSelectedNode();
20086         var a = [];
20087         if (!p) {
20088             a.push(p); // push blank onto stack..
20089             p = this.getParentElement();
20090         }
20091         
20092         
20093         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20094             a.push(p);
20095             p = p.parentNode;
20096         }
20097         a.push(this.doc.body);
20098         return a;
20099     },
20100     lastSel : false,
20101     lastSelNode : false,
20102     
20103     
20104     getSelection : function() 
20105     {
20106         this.assignDocWin();
20107         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20108     },
20109     
20110     getSelectedNode: function() 
20111     {
20112         // this may only work on Gecko!!!
20113         
20114         // should we cache this!!!!
20115         
20116         
20117         
20118          
20119         var range = this.createRange(this.getSelection()).cloneRange();
20120         
20121         if (Roo.isIE) {
20122             var parent = range.parentElement();
20123             while (true) {
20124                 var testRange = range.duplicate();
20125                 testRange.moveToElementText(parent);
20126                 if (testRange.inRange(range)) {
20127                     break;
20128                 }
20129                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20130                     break;
20131                 }
20132                 parent = parent.parentElement;
20133             }
20134             return parent;
20135         }
20136         
20137         // is ancestor a text element.
20138         var ac =  range.commonAncestorContainer;
20139         if (ac.nodeType == 3) {
20140             ac = ac.parentNode;
20141         }
20142         
20143         var ar = ac.childNodes;
20144          
20145         var nodes = [];
20146         var other_nodes = [];
20147         var has_other_nodes = false;
20148         for (var i=0;i<ar.length;i++) {
20149             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20150                 continue;
20151             }
20152             // fullly contained node.
20153             
20154             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20155                 nodes.push(ar[i]);
20156                 continue;
20157             }
20158             
20159             // probably selected..
20160             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20161                 other_nodes.push(ar[i]);
20162                 continue;
20163             }
20164             // outer..
20165             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20166                 continue;
20167             }
20168             
20169             
20170             has_other_nodes = true;
20171         }
20172         if (!nodes.length && other_nodes.length) {
20173             nodes= other_nodes;
20174         }
20175         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20176             return false;
20177         }
20178         
20179         return nodes[0];
20180     },
20181     createRange: function(sel)
20182     {
20183         // this has strange effects when using with 
20184         // top toolbar - not sure if it's a great idea.
20185         //this.editor.contentWindow.focus();
20186         if (typeof sel != "undefined") {
20187             try {
20188                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20189             } catch(e) {
20190                 return this.doc.createRange();
20191             }
20192         } else {
20193             return this.doc.createRange();
20194         }
20195     },
20196     getParentElement: function()
20197     {
20198         
20199         this.assignDocWin();
20200         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20201         
20202         var range = this.createRange(sel);
20203          
20204         try {
20205             var p = range.commonAncestorContainer;
20206             while (p.nodeType == 3) { // text node
20207                 p = p.parentNode;
20208             }
20209             return p;
20210         } catch (e) {
20211             return null;
20212         }
20213     
20214     },
20215     /***
20216      *
20217      * Range intersection.. the hard stuff...
20218      *  '-1' = before
20219      *  '0' = hits..
20220      *  '1' = after.
20221      *         [ -- selected range --- ]
20222      *   [fail]                        [fail]
20223      *
20224      *    basically..
20225      *      if end is before start or  hits it. fail.
20226      *      if start is after end or hits it fail.
20227      *
20228      *   if either hits (but other is outside. - then it's not 
20229      *   
20230      *    
20231      **/
20232     
20233     
20234     // @see http://www.thismuchiknow.co.uk/?p=64.
20235     rangeIntersectsNode : function(range, node)
20236     {
20237         var nodeRange = node.ownerDocument.createRange();
20238         try {
20239             nodeRange.selectNode(node);
20240         } catch (e) {
20241             nodeRange.selectNodeContents(node);
20242         }
20243     
20244         var rangeStartRange = range.cloneRange();
20245         rangeStartRange.collapse(true);
20246     
20247         var rangeEndRange = range.cloneRange();
20248         rangeEndRange.collapse(false);
20249     
20250         var nodeStartRange = nodeRange.cloneRange();
20251         nodeStartRange.collapse(true);
20252     
20253         var nodeEndRange = nodeRange.cloneRange();
20254         nodeEndRange.collapse(false);
20255     
20256         return rangeStartRange.compareBoundaryPoints(
20257                  Range.START_TO_START, nodeEndRange) == -1 &&
20258                rangeEndRange.compareBoundaryPoints(
20259                  Range.START_TO_START, nodeStartRange) == 1;
20260         
20261          
20262     },
20263     rangeCompareNode : function(range, node)
20264     {
20265         var nodeRange = node.ownerDocument.createRange();
20266         try {
20267             nodeRange.selectNode(node);
20268         } catch (e) {
20269             nodeRange.selectNodeContents(node);
20270         }
20271         
20272         
20273         range.collapse(true);
20274     
20275         nodeRange.collapse(true);
20276      
20277         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20278         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20279          
20280         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20281         
20282         var nodeIsBefore   =  ss == 1;
20283         var nodeIsAfter    = ee == -1;
20284         
20285         if (nodeIsBefore && nodeIsAfter) {
20286             return 0; // outer
20287         }
20288         if (!nodeIsBefore && nodeIsAfter) {
20289             return 1; //right trailed.
20290         }
20291         
20292         if (nodeIsBefore && !nodeIsAfter) {
20293             return 2;  // left trailed.
20294         }
20295         // fully contined.
20296         return 3;
20297     },
20298
20299     // private? - in a new class?
20300     cleanUpPaste :  function()
20301     {
20302         // cleans up the whole document..
20303         Roo.log('cleanuppaste');
20304         
20305         this.cleanUpChildren(this.doc.body);
20306         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20307         if (clean != this.doc.body.innerHTML) {
20308             this.doc.body.innerHTML = clean;
20309         }
20310         
20311     },
20312     
20313     cleanWordChars : function(input) {// change the chars to hex code
20314         var he = Roo.HtmlEditorCore;
20315         
20316         var output = input;
20317         Roo.each(he.swapCodes, function(sw) { 
20318             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20319             
20320             output = output.replace(swapper, sw[1]);
20321         });
20322         
20323         return output;
20324     },
20325     
20326     
20327     cleanUpChildren : function (n)
20328     {
20329         if (!n.childNodes.length) {
20330             return;
20331         }
20332         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20333            this.cleanUpChild(n.childNodes[i]);
20334         }
20335     },
20336     
20337     
20338         
20339     
20340     cleanUpChild : function (node)
20341     {
20342         var ed = this;
20343         //console.log(node);
20344         if (node.nodeName == "#text") {
20345             // clean up silly Windows -- stuff?
20346             return; 
20347         }
20348         if (node.nodeName == "#comment") {
20349             node.parentNode.removeChild(node);
20350             // clean up silly Windows -- stuff?
20351             return; 
20352         }
20353         var lcname = node.tagName.toLowerCase();
20354         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20355         // whitelist of tags..
20356         
20357         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20358             // remove node.
20359             node.parentNode.removeChild(node);
20360             return;
20361             
20362         }
20363         
20364         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20365         
20366         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20367         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20368         
20369         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20370         //    remove_keep_children = true;
20371         //}
20372         
20373         if (remove_keep_children) {
20374             this.cleanUpChildren(node);
20375             // inserts everything just before this node...
20376             while (node.childNodes.length) {
20377                 var cn = node.childNodes[0];
20378                 node.removeChild(cn);
20379                 node.parentNode.insertBefore(cn, node);
20380             }
20381             node.parentNode.removeChild(node);
20382             return;
20383         }
20384         
20385         if (!node.attributes || !node.attributes.length) {
20386             this.cleanUpChildren(node);
20387             return;
20388         }
20389         
20390         function cleanAttr(n,v)
20391         {
20392             
20393             if (v.match(/^\./) || v.match(/^\//)) {
20394                 return;
20395             }
20396             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20397                 return;
20398             }
20399             if (v.match(/^#/)) {
20400                 return;
20401             }
20402 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20403             node.removeAttribute(n);
20404             
20405         }
20406         
20407         var cwhite = this.cwhite;
20408         var cblack = this.cblack;
20409             
20410         function cleanStyle(n,v)
20411         {
20412             if (v.match(/expression/)) { //XSS?? should we even bother..
20413                 node.removeAttribute(n);
20414                 return;
20415             }
20416             
20417             var parts = v.split(/;/);
20418             var clean = [];
20419             
20420             Roo.each(parts, function(p) {
20421                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20422                 if (!p.length) {
20423                     return true;
20424                 }
20425                 var l = p.split(':').shift().replace(/\s+/g,'');
20426                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20427                 
20428                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20429 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20430                     //node.removeAttribute(n);
20431                     return true;
20432                 }
20433                 //Roo.log()
20434                 // only allow 'c whitelisted system attributes'
20435                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20436 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20437                     //node.removeAttribute(n);
20438                     return true;
20439                 }
20440                 
20441                 
20442                  
20443                 
20444                 clean.push(p);
20445                 return true;
20446             });
20447             if (clean.length) { 
20448                 node.setAttribute(n, clean.join(';'));
20449             } else {
20450                 node.removeAttribute(n);
20451             }
20452             
20453         }
20454         
20455         
20456         for (var i = node.attributes.length-1; i > -1 ; i--) {
20457             var a = node.attributes[i];
20458             //console.log(a);
20459             
20460             if (a.name.toLowerCase().substr(0,2)=='on')  {
20461                 node.removeAttribute(a.name);
20462                 continue;
20463             }
20464             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20465                 node.removeAttribute(a.name);
20466                 continue;
20467             }
20468             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20469                 cleanAttr(a.name,a.value); // fixme..
20470                 continue;
20471             }
20472             if (a.name == 'style') {
20473                 cleanStyle(a.name,a.value);
20474                 continue;
20475             }
20476             /// clean up MS crap..
20477             // tecnically this should be a list of valid class'es..
20478             
20479             
20480             if (a.name == 'class') {
20481                 if (a.value.match(/^Mso/)) {
20482                     node.className = '';
20483                 }
20484                 
20485                 if (a.value.match(/body/)) {
20486                     node.className = '';
20487                 }
20488                 continue;
20489             }
20490             
20491             // style cleanup!?
20492             // class cleanup?
20493             
20494         }
20495         
20496         
20497         this.cleanUpChildren(node);
20498         
20499         
20500     },
20501     
20502     /**
20503      * Clean up MS wordisms...
20504      */
20505     cleanWord : function(node)
20506     {
20507         
20508         
20509         if (!node) {
20510             this.cleanWord(this.doc.body);
20511             return;
20512         }
20513         if (node.nodeName == "#text") {
20514             // clean up silly Windows -- stuff?
20515             return; 
20516         }
20517         if (node.nodeName == "#comment") {
20518             node.parentNode.removeChild(node);
20519             // clean up silly Windows -- stuff?
20520             return; 
20521         }
20522         
20523         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20524             node.parentNode.removeChild(node);
20525             return;
20526         }
20527         
20528         // remove - but keep children..
20529         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20530             while (node.childNodes.length) {
20531                 var cn = node.childNodes[0];
20532                 node.removeChild(cn);
20533                 node.parentNode.insertBefore(cn, node);
20534             }
20535             node.parentNode.removeChild(node);
20536             this.iterateChildren(node, this.cleanWord);
20537             return;
20538         }
20539         // clean styles
20540         if (node.className.length) {
20541             
20542             var cn = node.className.split(/\W+/);
20543             var cna = [];
20544             Roo.each(cn, function(cls) {
20545                 if (cls.match(/Mso[a-zA-Z]+/)) {
20546                     return;
20547                 }
20548                 cna.push(cls);
20549             });
20550             node.className = cna.length ? cna.join(' ') : '';
20551             if (!cna.length) {
20552                 node.removeAttribute("class");
20553             }
20554         }
20555         
20556         if (node.hasAttribute("lang")) {
20557             node.removeAttribute("lang");
20558         }
20559         
20560         if (node.hasAttribute("style")) {
20561             
20562             var styles = node.getAttribute("style").split(";");
20563             var nstyle = [];
20564             Roo.each(styles, function(s) {
20565                 if (!s.match(/:/)) {
20566                     return;
20567                 }
20568                 var kv = s.split(":");
20569                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20570                     return;
20571                 }
20572                 // what ever is left... we allow.
20573                 nstyle.push(s);
20574             });
20575             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20576             if (!nstyle.length) {
20577                 node.removeAttribute('style');
20578             }
20579         }
20580         this.iterateChildren(node, this.cleanWord);
20581         
20582         
20583         
20584     },
20585     /**
20586      * iterateChildren of a Node, calling fn each time, using this as the scole..
20587      * @param {DomNode} node node to iterate children of.
20588      * @param {Function} fn method of this class to call on each item.
20589      */
20590     iterateChildren : function(node, fn)
20591     {
20592         if (!node.childNodes.length) {
20593                 return;
20594         }
20595         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20596            fn.call(this, node.childNodes[i])
20597         }
20598     },
20599     
20600     
20601     /**
20602      * cleanTableWidths.
20603      *
20604      * Quite often pasting from word etc.. results in tables with column and widths.
20605      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20606      *
20607      */
20608     cleanTableWidths : function(node)
20609     {
20610          
20611          
20612         if (!node) {
20613             this.cleanTableWidths(this.doc.body);
20614             return;
20615         }
20616         
20617         // ignore list...
20618         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20619             return; 
20620         }
20621         Roo.log(node.tagName);
20622         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20623             this.iterateChildren(node, this.cleanTableWidths);
20624             return;
20625         }
20626         if (node.hasAttribute('width')) {
20627             node.removeAttribute('width');
20628         }
20629         
20630          
20631         if (node.hasAttribute("style")) {
20632             // pretty basic...
20633             
20634             var styles = node.getAttribute("style").split(";");
20635             var nstyle = [];
20636             Roo.each(styles, function(s) {
20637                 if (!s.match(/:/)) {
20638                     return;
20639                 }
20640                 var kv = s.split(":");
20641                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20642                     return;
20643                 }
20644                 // what ever is left... we allow.
20645                 nstyle.push(s);
20646             });
20647             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20648             if (!nstyle.length) {
20649                 node.removeAttribute('style');
20650             }
20651         }
20652         
20653         this.iterateChildren(node, this.cleanTableWidths);
20654         
20655         
20656     },
20657     
20658     
20659     
20660     
20661     domToHTML : function(currentElement, depth, nopadtext) {
20662         
20663         depth = depth || 0;
20664         nopadtext = nopadtext || false;
20665     
20666         if (!currentElement) {
20667             return this.domToHTML(this.doc.body);
20668         }
20669         
20670         //Roo.log(currentElement);
20671         var j;
20672         var allText = false;
20673         var nodeName = currentElement.nodeName;
20674         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
20675         
20676         if  (nodeName == '#text') {
20677             
20678             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
20679         }
20680         
20681         
20682         var ret = '';
20683         if (nodeName != 'BODY') {
20684              
20685             var i = 0;
20686             // Prints the node tagName, such as <A>, <IMG>, etc
20687             if (tagName) {
20688                 var attr = [];
20689                 for(i = 0; i < currentElement.attributes.length;i++) {
20690                     // quoting?
20691                     var aname = currentElement.attributes.item(i).name;
20692                     if (!currentElement.attributes.item(i).value.length) {
20693                         continue;
20694                     }
20695                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
20696                 }
20697                 
20698                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
20699             } 
20700             else {
20701                 
20702                 // eack
20703             }
20704         } else {
20705             tagName = false;
20706         }
20707         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
20708             return ret;
20709         }
20710         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
20711             nopadtext = true;
20712         }
20713         
20714         
20715         // Traverse the tree
20716         i = 0;
20717         var currentElementChild = currentElement.childNodes.item(i);
20718         var allText = true;
20719         var innerHTML  = '';
20720         lastnode = '';
20721         while (currentElementChild) {
20722             // Formatting code (indent the tree so it looks nice on the screen)
20723             var nopad = nopadtext;
20724             if (lastnode == 'SPAN') {
20725                 nopad  = true;
20726             }
20727             // text
20728             if  (currentElementChild.nodeName == '#text') {
20729                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
20730                 toadd = nopadtext ? toadd : toadd.trim();
20731                 if (!nopad && toadd.length > 80) {
20732                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
20733                 }
20734                 innerHTML  += toadd;
20735                 
20736                 i++;
20737                 currentElementChild = currentElement.childNodes.item(i);
20738                 lastNode = '';
20739                 continue;
20740             }
20741             allText = false;
20742             
20743             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
20744                 
20745             // Recursively traverse the tree structure of the child node
20746             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
20747             lastnode = currentElementChild.nodeName;
20748             i++;
20749             currentElementChild=currentElement.childNodes.item(i);
20750         }
20751         
20752         ret += innerHTML;
20753         
20754         if (!allText) {
20755                 // The remaining code is mostly for formatting the tree
20756             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
20757         }
20758         
20759         
20760         if (tagName) {
20761             ret+= "</"+tagName+">";
20762         }
20763         return ret;
20764         
20765     },
20766         
20767     applyBlacklists : function()
20768     {
20769         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
20770         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
20771         
20772         this.white = [];
20773         this.black = [];
20774         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
20775             if (b.indexOf(tag) > -1) {
20776                 return;
20777             }
20778             this.white.push(tag);
20779             
20780         }, this);
20781         
20782         Roo.each(w, function(tag) {
20783             if (b.indexOf(tag) > -1) {
20784                 return;
20785             }
20786             if (this.white.indexOf(tag) > -1) {
20787                 return;
20788             }
20789             this.white.push(tag);
20790             
20791         }, this);
20792         
20793         
20794         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
20795             if (w.indexOf(tag) > -1) {
20796                 return;
20797             }
20798             this.black.push(tag);
20799             
20800         }, this);
20801         
20802         Roo.each(b, function(tag) {
20803             if (w.indexOf(tag) > -1) {
20804                 return;
20805             }
20806             if (this.black.indexOf(tag) > -1) {
20807                 return;
20808             }
20809             this.black.push(tag);
20810             
20811         }, this);
20812         
20813         
20814         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
20815         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
20816         
20817         this.cwhite = [];
20818         this.cblack = [];
20819         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
20820             if (b.indexOf(tag) > -1) {
20821                 return;
20822             }
20823             this.cwhite.push(tag);
20824             
20825         }, this);
20826         
20827         Roo.each(w, function(tag) {
20828             if (b.indexOf(tag) > -1) {
20829                 return;
20830             }
20831             if (this.cwhite.indexOf(tag) > -1) {
20832                 return;
20833             }
20834             this.cwhite.push(tag);
20835             
20836         }, this);
20837         
20838         
20839         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
20840             if (w.indexOf(tag) > -1) {
20841                 return;
20842             }
20843             this.cblack.push(tag);
20844             
20845         }, this);
20846         
20847         Roo.each(b, function(tag) {
20848             if (w.indexOf(tag) > -1) {
20849                 return;
20850             }
20851             if (this.cblack.indexOf(tag) > -1) {
20852                 return;
20853             }
20854             this.cblack.push(tag);
20855             
20856         }, this);
20857     },
20858     
20859     setStylesheets : function(stylesheets)
20860     {
20861         if(typeof(stylesheets) == 'string'){
20862             Roo.get(this.iframe.contentDocument.head).createChild({
20863                 tag : 'link',
20864                 rel : 'stylesheet',
20865                 type : 'text/css',
20866                 href : stylesheets
20867             });
20868             
20869             return;
20870         }
20871         var _this = this;
20872      
20873         Roo.each(stylesheets, function(s) {
20874             if(!s.length){
20875                 return;
20876             }
20877             
20878             Roo.get(_this.iframe.contentDocument.head).createChild({
20879                 tag : 'link',
20880                 rel : 'stylesheet',
20881                 type : 'text/css',
20882                 href : s
20883             });
20884         });
20885
20886         
20887     },
20888     
20889     removeStylesheets : function()
20890     {
20891         var _this = this;
20892         
20893         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
20894             s.remove();
20895         });
20896     }
20897     
20898     // hide stuff that is not compatible
20899     /**
20900      * @event blur
20901      * @hide
20902      */
20903     /**
20904      * @event change
20905      * @hide
20906      */
20907     /**
20908      * @event focus
20909      * @hide
20910      */
20911     /**
20912      * @event specialkey
20913      * @hide
20914      */
20915     /**
20916      * @cfg {String} fieldClass @hide
20917      */
20918     /**
20919      * @cfg {String} focusClass @hide
20920      */
20921     /**
20922      * @cfg {String} autoCreate @hide
20923      */
20924     /**
20925      * @cfg {String} inputType @hide
20926      */
20927     /**
20928      * @cfg {String} invalidClass @hide
20929      */
20930     /**
20931      * @cfg {String} invalidText @hide
20932      */
20933     /**
20934      * @cfg {String} msgFx @hide
20935      */
20936     /**
20937      * @cfg {String} validateOnBlur @hide
20938      */
20939 });
20940
20941 Roo.HtmlEditorCore.white = [
20942         'area', 'br', 'img', 'input', 'hr', 'wbr',
20943         
20944        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
20945        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
20946        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
20947        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
20948        'table',   'ul',         'xmp', 
20949        
20950        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
20951       'thead',   'tr', 
20952      
20953       'dir', 'menu', 'ol', 'ul', 'dl',
20954        
20955       'embed',  'object'
20956 ];
20957
20958
20959 Roo.HtmlEditorCore.black = [
20960     //    'embed',  'object', // enable - backend responsiblity to clean thiese
20961         'applet', // 
20962         'base',   'basefont', 'bgsound', 'blink',  'body', 
20963         'frame',  'frameset', 'head',    'html',   'ilayer', 
20964         'iframe', 'layer',  'link',     'meta',    'object',   
20965         'script', 'style' ,'title',  'xml' // clean later..
20966 ];
20967 Roo.HtmlEditorCore.clean = [
20968     'script', 'style', 'title', 'xml'
20969 ];
20970 Roo.HtmlEditorCore.remove = [
20971     'font'
20972 ];
20973 // attributes..
20974
20975 Roo.HtmlEditorCore.ablack = [
20976     'on'
20977 ];
20978     
20979 Roo.HtmlEditorCore.aclean = [ 
20980     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
20981 ];
20982
20983 // protocols..
20984 Roo.HtmlEditorCore.pwhite= [
20985         'http',  'https',  'mailto'
20986 ];
20987
20988 // white listed style attributes.
20989 Roo.HtmlEditorCore.cwhite= [
20990       //  'text-align', /// default is to allow most things..
20991       
20992          
20993 //        'font-size'//??
20994 ];
20995
20996 // black listed style attributes.
20997 Roo.HtmlEditorCore.cblack= [
20998       //  'font-size' -- this can be set by the project 
20999 ];
21000
21001
21002 Roo.HtmlEditorCore.swapCodes   =[ 
21003     [    8211, "--" ], 
21004     [    8212, "--" ], 
21005     [    8216,  "'" ],  
21006     [    8217, "'" ],  
21007     [    8220, '"' ],  
21008     [    8221, '"' ],  
21009     [    8226, "*" ],  
21010     [    8230, "..." ]
21011 ]; 
21012
21013     /*
21014  * - LGPL
21015  *
21016  * HtmlEditor
21017  * 
21018  */
21019
21020 /**
21021  * @class Roo.bootstrap.HtmlEditor
21022  * @extends Roo.bootstrap.TextArea
21023  * Bootstrap HtmlEditor class
21024
21025  * @constructor
21026  * Create a new HtmlEditor
21027  * @param {Object} config The config object
21028  */
21029
21030 Roo.bootstrap.HtmlEditor = function(config){
21031     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21032     if (!this.toolbars) {
21033         this.toolbars = [];
21034     }
21035     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21036     this.addEvents({
21037             /**
21038              * @event initialize
21039              * Fires when the editor is fully initialized (including the iframe)
21040              * @param {HtmlEditor} this
21041              */
21042             initialize: true,
21043             /**
21044              * @event activate
21045              * Fires when the editor is first receives the focus. Any insertion must wait
21046              * until after this event.
21047              * @param {HtmlEditor} this
21048              */
21049             activate: true,
21050              /**
21051              * @event beforesync
21052              * Fires before the textarea is updated with content from the editor iframe. Return false
21053              * to cancel the sync.
21054              * @param {HtmlEditor} this
21055              * @param {String} html
21056              */
21057             beforesync: true,
21058              /**
21059              * @event beforepush
21060              * Fires before the iframe editor is updated with content from the textarea. Return false
21061              * to cancel the push.
21062              * @param {HtmlEditor} this
21063              * @param {String} html
21064              */
21065             beforepush: true,
21066              /**
21067              * @event sync
21068              * Fires when the textarea is updated with content from the editor iframe.
21069              * @param {HtmlEditor} this
21070              * @param {String} html
21071              */
21072             sync: true,
21073              /**
21074              * @event push
21075              * Fires when the iframe editor is updated with content from the textarea.
21076              * @param {HtmlEditor} this
21077              * @param {String} html
21078              */
21079             push: true,
21080              /**
21081              * @event editmodechange
21082              * Fires when the editor switches edit modes
21083              * @param {HtmlEditor} this
21084              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21085              */
21086             editmodechange: true,
21087             /**
21088              * @event editorevent
21089              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21090              * @param {HtmlEditor} this
21091              */
21092             editorevent: true,
21093             /**
21094              * @event firstfocus
21095              * Fires when on first focus - needed by toolbars..
21096              * @param {HtmlEditor} this
21097              */
21098             firstfocus: true,
21099             /**
21100              * @event autosave
21101              * Auto save the htmlEditor value as a file into Events
21102              * @param {HtmlEditor} this
21103              */
21104             autosave: true,
21105             /**
21106              * @event savedpreview
21107              * preview the saved version of htmlEditor
21108              * @param {HtmlEditor} this
21109              */
21110             savedpreview: true
21111         });
21112 };
21113
21114
21115 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21116     
21117     
21118       /**
21119      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21120      */
21121     toolbars : false,
21122    
21123      /**
21124      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21125      *                        Roo.resizable.
21126      */
21127     resizable : false,
21128      /**
21129      * @cfg {Number} height (in pixels)
21130      */   
21131     height: 300,
21132    /**
21133      * @cfg {Number} width (in pixels)
21134      */   
21135     width: false,
21136     
21137     /**
21138      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21139      * 
21140      */
21141     stylesheets: false,
21142     
21143     // id of frame..
21144     frameId: false,
21145     
21146     // private properties
21147     validationEvent : false,
21148     deferHeight: true,
21149     initialized : false,
21150     activated : false,
21151     
21152     onFocus : Roo.emptyFn,
21153     iframePad:3,
21154     hideMode:'offsets',
21155     
21156     
21157     tbContainer : false,
21158     
21159     toolbarContainer :function() {
21160         return this.wrap.select('.x-html-editor-tb',true).first();
21161     },
21162
21163     /**
21164      * Protected method that will not generally be called directly. It
21165      * is called when the editor creates its toolbar. Override this method if you need to
21166      * add custom toolbar buttons.
21167      * @param {HtmlEditor} editor
21168      */
21169     createToolbar : function(){
21170         
21171         Roo.log("create toolbars");
21172         
21173         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21174         this.toolbars[0].render(this.toolbarContainer());
21175         
21176         return;
21177         
21178 //        if (!editor.toolbars || !editor.toolbars.length) {
21179 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21180 //        }
21181 //        
21182 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21183 //            editor.toolbars[i] = Roo.factory(
21184 //                    typeof(editor.toolbars[i]) == 'string' ?
21185 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21186 //                Roo.bootstrap.HtmlEditor);
21187 //            editor.toolbars[i].init(editor);
21188 //        }
21189     },
21190
21191      
21192     // private
21193     onRender : function(ct, position)
21194     {
21195        // Roo.log("Call onRender: " + this.xtype);
21196         var _t = this;
21197         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21198       
21199         this.wrap = this.inputEl().wrap({
21200             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21201         });
21202         
21203         this.editorcore.onRender(ct, position);
21204          
21205         if (this.resizable) {
21206             this.resizeEl = new Roo.Resizable(this.wrap, {
21207                 pinned : true,
21208                 wrap: true,
21209                 dynamic : true,
21210                 minHeight : this.height,
21211                 height: this.height,
21212                 handles : this.resizable,
21213                 width: this.width,
21214                 listeners : {
21215                     resize : function(r, w, h) {
21216                         _t.onResize(w,h); // -something
21217                     }
21218                 }
21219             });
21220             
21221         }
21222         this.createToolbar(this);
21223        
21224         
21225         if(!this.width && this.resizable){
21226             this.setSize(this.wrap.getSize());
21227         }
21228         if (this.resizeEl) {
21229             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21230             // should trigger onReize..
21231         }
21232         
21233     },
21234
21235     // private
21236     onResize : function(w, h)
21237     {
21238         Roo.log('resize: ' +w + ',' + h );
21239         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21240         var ew = false;
21241         var eh = false;
21242         
21243         if(this.inputEl() ){
21244             if(typeof w == 'number'){
21245                 var aw = w - this.wrap.getFrameWidth('lr');
21246                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21247                 ew = aw;
21248             }
21249             if(typeof h == 'number'){
21250                  var tbh = -11;  // fixme it needs to tool bar size!
21251                 for (var i =0; i < this.toolbars.length;i++) {
21252                     // fixme - ask toolbars for heights?
21253                     tbh += this.toolbars[i].el.getHeight();
21254                     //if (this.toolbars[i].footer) {
21255                     //    tbh += this.toolbars[i].footer.el.getHeight();
21256                     //}
21257                 }
21258               
21259                 
21260                 
21261                 
21262                 
21263                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21264                 ah -= 5; // knock a few pixes off for look..
21265                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21266                 var eh = ah;
21267             }
21268         }
21269         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21270         this.editorcore.onResize(ew,eh);
21271         
21272     },
21273
21274     /**
21275      * Toggles the editor between standard and source edit mode.
21276      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21277      */
21278     toggleSourceEdit : function(sourceEditMode)
21279     {
21280         this.editorcore.toggleSourceEdit(sourceEditMode);
21281         
21282         if(this.editorcore.sourceEditMode){
21283             Roo.log('editor - showing textarea');
21284             
21285 //            Roo.log('in');
21286 //            Roo.log(this.syncValue());
21287             this.syncValue();
21288             this.inputEl().removeClass(['hide', 'x-hidden']);
21289             this.inputEl().dom.removeAttribute('tabIndex');
21290             this.inputEl().focus();
21291         }else{
21292             Roo.log('editor - hiding textarea');
21293 //            Roo.log('out')
21294 //            Roo.log(this.pushValue()); 
21295             this.pushValue();
21296             
21297             this.inputEl().addClass(['hide', 'x-hidden']);
21298             this.inputEl().dom.setAttribute('tabIndex', -1);
21299             //this.deferFocus();
21300         }
21301          
21302         if(this.resizable){
21303             this.setSize(this.wrap.getSize());
21304         }
21305         
21306         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21307     },
21308  
21309     // private (for BoxComponent)
21310     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21311
21312     // private (for BoxComponent)
21313     getResizeEl : function(){
21314         return this.wrap;
21315     },
21316
21317     // private (for BoxComponent)
21318     getPositionEl : function(){
21319         return this.wrap;
21320     },
21321
21322     // private
21323     initEvents : function(){
21324         this.originalValue = this.getValue();
21325     },
21326
21327 //    /**
21328 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21329 //     * @method
21330 //     */
21331 //    markInvalid : Roo.emptyFn,
21332 //    /**
21333 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21334 //     * @method
21335 //     */
21336 //    clearInvalid : Roo.emptyFn,
21337
21338     setValue : function(v){
21339         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21340         this.editorcore.pushValue();
21341     },
21342
21343      
21344     // private
21345     deferFocus : function(){
21346         this.focus.defer(10, this);
21347     },
21348
21349     // doc'ed in Field
21350     focus : function(){
21351         this.editorcore.focus();
21352         
21353     },
21354       
21355
21356     // private
21357     onDestroy : function(){
21358         
21359         
21360         
21361         if(this.rendered){
21362             
21363             for (var i =0; i < this.toolbars.length;i++) {
21364                 // fixme - ask toolbars for heights?
21365                 this.toolbars[i].onDestroy();
21366             }
21367             
21368             this.wrap.dom.innerHTML = '';
21369             this.wrap.remove();
21370         }
21371     },
21372
21373     // private
21374     onFirstFocus : function(){
21375         //Roo.log("onFirstFocus");
21376         this.editorcore.onFirstFocus();
21377          for (var i =0; i < this.toolbars.length;i++) {
21378             this.toolbars[i].onFirstFocus();
21379         }
21380         
21381     },
21382     
21383     // private
21384     syncValue : function()
21385     {   
21386         this.editorcore.syncValue();
21387     },
21388     
21389     pushValue : function()
21390     {   
21391         this.editorcore.pushValue();
21392     }
21393      
21394     
21395     // hide stuff that is not compatible
21396     /**
21397      * @event blur
21398      * @hide
21399      */
21400     /**
21401      * @event change
21402      * @hide
21403      */
21404     /**
21405      * @event focus
21406      * @hide
21407      */
21408     /**
21409      * @event specialkey
21410      * @hide
21411      */
21412     /**
21413      * @cfg {String} fieldClass @hide
21414      */
21415     /**
21416      * @cfg {String} focusClass @hide
21417      */
21418     /**
21419      * @cfg {String} autoCreate @hide
21420      */
21421     /**
21422      * @cfg {String} inputType @hide
21423      */
21424     /**
21425      * @cfg {String} invalidClass @hide
21426      */
21427     /**
21428      * @cfg {String} invalidText @hide
21429      */
21430     /**
21431      * @cfg {String} msgFx @hide
21432      */
21433     /**
21434      * @cfg {String} validateOnBlur @hide
21435      */
21436 });
21437  
21438     
21439    
21440    
21441    
21442       
21443 Roo.namespace('Roo.bootstrap.htmleditor');
21444 /**
21445  * @class Roo.bootstrap.HtmlEditorToolbar1
21446  * Basic Toolbar
21447  * 
21448  * Usage:
21449  *
21450  new Roo.bootstrap.HtmlEditor({
21451     ....
21452     toolbars : [
21453         new Roo.bootstrap.HtmlEditorToolbar1({
21454             disable : { fonts: 1 , format: 1, ..., ... , ...],
21455             btns : [ .... ]
21456         })
21457     }
21458      
21459  * 
21460  * @cfg {Object} disable List of elements to disable..
21461  * @cfg {Array} btns List of additional buttons.
21462  * 
21463  * 
21464  * NEEDS Extra CSS? 
21465  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21466  */
21467  
21468 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21469 {
21470     
21471     Roo.apply(this, config);
21472     
21473     // default disabled, based on 'good practice'..
21474     this.disable = this.disable || {};
21475     Roo.applyIf(this.disable, {
21476         fontSize : true,
21477         colors : true,
21478         specialElements : true
21479     });
21480     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21481     
21482     this.editor = config.editor;
21483     this.editorcore = config.editor.editorcore;
21484     
21485     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21486     
21487     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21488     // dont call parent... till later.
21489 }
21490 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21491      
21492     bar : true,
21493     
21494     editor : false,
21495     editorcore : false,
21496     
21497     
21498     formats : [
21499         "p" ,  
21500         "h1","h2","h3","h4","h5","h6", 
21501         "pre", "code", 
21502         "abbr", "acronym", "address", "cite", "samp", "var",
21503         'div','span'
21504     ],
21505     
21506     onRender : function(ct, position)
21507     {
21508        // Roo.log("Call onRender: " + this.xtype);
21509         
21510        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21511        Roo.log(this.el);
21512        this.el.dom.style.marginBottom = '0';
21513        var _this = this;
21514        var editorcore = this.editorcore;
21515        var editor= this.editor;
21516        
21517        var children = [];
21518        var btn = function(id,cmd , toggle, handler){
21519        
21520             var  event = toggle ? 'toggle' : 'click';
21521        
21522             var a = {
21523                 size : 'sm',
21524                 xtype: 'Button',
21525                 xns: Roo.bootstrap,
21526                 glyphicon : id,
21527                 cmd : id || cmd,
21528                 enableToggle:toggle !== false,
21529                 //html : 'submit'
21530                 pressed : toggle ? false : null,
21531                 listeners : {}
21532             };
21533             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21534                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21535             };
21536             children.push(a);
21537             return a;
21538        }
21539         
21540         var style = {
21541                 xtype: 'Button',
21542                 size : 'sm',
21543                 xns: Roo.bootstrap,
21544                 glyphicon : 'font',
21545                 //html : 'submit'
21546                 menu : {
21547                     xtype: 'Menu',
21548                     xns: Roo.bootstrap,
21549                     items:  []
21550                 }
21551         };
21552         Roo.each(this.formats, function(f) {
21553             style.menu.items.push({
21554                 xtype :'MenuItem',
21555                 xns: Roo.bootstrap,
21556                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21557                 tagname : f,
21558                 listeners : {
21559                     click : function()
21560                     {
21561                         editorcore.insertTag(this.tagname);
21562                         editor.focus();
21563                     }
21564                 }
21565                 
21566             });
21567         });
21568          children.push(style);   
21569             
21570             
21571         btn('bold',false,true);
21572         btn('italic',false,true);
21573         btn('align-left', 'justifyleft',true);
21574         btn('align-center', 'justifycenter',true);
21575         btn('align-right' , 'justifyright',true);
21576         btn('link', false, false, function(btn) {
21577             //Roo.log("create link?");
21578             var url = prompt(this.createLinkText, this.defaultLinkValue);
21579             if(url && url != 'http:/'+'/'){
21580                 this.editorcore.relayCmd('createlink', url);
21581             }
21582         }),
21583         btn('list','insertunorderedlist',true);
21584         btn('pencil', false,true, function(btn){
21585                 Roo.log(this);
21586                 
21587                 this.toggleSourceEdit(btn.pressed);
21588         });
21589         /*
21590         var cog = {
21591                 xtype: 'Button',
21592                 size : 'sm',
21593                 xns: Roo.bootstrap,
21594                 glyphicon : 'cog',
21595                 //html : 'submit'
21596                 menu : {
21597                     xtype: 'Menu',
21598                     xns: Roo.bootstrap,
21599                     items:  []
21600                 }
21601         };
21602         
21603         cog.menu.items.push({
21604             xtype :'MenuItem',
21605             xns: Roo.bootstrap,
21606             html : Clean styles,
21607             tagname : f,
21608             listeners : {
21609                 click : function()
21610                 {
21611                     editorcore.insertTag(this.tagname);
21612                     editor.focus();
21613                 }
21614             }
21615             
21616         });
21617        */
21618         
21619          
21620        this.xtype = 'NavSimplebar';
21621         
21622         for(var i=0;i< children.length;i++) {
21623             
21624             this.buttons.add(this.addxtypeChild(children[i]));
21625             
21626         }
21627         
21628         editor.on('editorevent', this.updateToolbar, this);
21629     },
21630     onBtnClick : function(id)
21631     {
21632        this.editorcore.relayCmd(id);
21633        this.editorcore.focus();
21634     },
21635     
21636     /**
21637      * Protected method that will not generally be called directly. It triggers
21638      * a toolbar update by reading the markup state of the current selection in the editor.
21639      */
21640     updateToolbar: function(){
21641
21642         if(!this.editorcore.activated){
21643             this.editor.onFirstFocus(); // is this neeed?
21644             return;
21645         }
21646
21647         var btns = this.buttons; 
21648         var doc = this.editorcore.doc;
21649         btns.get('bold').setActive(doc.queryCommandState('bold'));
21650         btns.get('italic').setActive(doc.queryCommandState('italic'));
21651         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21652         
21653         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21654         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21655         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21656         
21657         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21658         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21659          /*
21660         
21661         var ans = this.editorcore.getAllAncestors();
21662         if (this.formatCombo) {
21663             
21664             
21665             var store = this.formatCombo.store;
21666             this.formatCombo.setValue("");
21667             for (var i =0; i < ans.length;i++) {
21668                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21669                     // select it..
21670                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21671                     break;
21672                 }
21673             }
21674         }
21675         
21676         
21677         
21678         // hides menus... - so this cant be on a menu...
21679         Roo.bootstrap.MenuMgr.hideAll();
21680         */
21681         Roo.bootstrap.MenuMgr.hideAll();
21682         //this.editorsyncValue();
21683     },
21684     onFirstFocus: function() {
21685         this.buttons.each(function(item){
21686            item.enable();
21687         });
21688     },
21689     toggleSourceEdit : function(sourceEditMode){
21690         
21691           
21692         if(sourceEditMode){
21693             Roo.log("disabling buttons");
21694            this.buttons.each( function(item){
21695                 if(item.cmd != 'pencil'){
21696                     item.disable();
21697                 }
21698             });
21699           
21700         }else{
21701             Roo.log("enabling buttons");
21702             if(this.editorcore.initialized){
21703                 this.buttons.each( function(item){
21704                     item.enable();
21705                 });
21706             }
21707             
21708         }
21709         Roo.log("calling toggole on editor");
21710         // tell the editor that it's been pressed..
21711         this.editor.toggleSourceEdit(sourceEditMode);
21712        
21713     }
21714 });
21715
21716
21717
21718
21719
21720 /**
21721  * @class Roo.bootstrap.Table.AbstractSelectionModel
21722  * @extends Roo.util.Observable
21723  * Abstract base class for grid SelectionModels.  It provides the interface that should be
21724  * implemented by descendant classes.  This class should not be directly instantiated.
21725  * @constructor
21726  */
21727 Roo.bootstrap.Table.AbstractSelectionModel = function(){
21728     this.locked = false;
21729     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
21730 };
21731
21732
21733 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
21734     /** @ignore Called by the grid automatically. Do not call directly. */
21735     init : function(grid){
21736         this.grid = grid;
21737         this.initEvents();
21738     },
21739
21740     /**
21741      * Locks the selections.
21742      */
21743     lock : function(){
21744         this.locked = true;
21745     },
21746
21747     /**
21748      * Unlocks the selections.
21749      */
21750     unlock : function(){
21751         this.locked = false;
21752     },
21753
21754     /**
21755      * Returns true if the selections are locked.
21756      * @return {Boolean}
21757      */
21758     isLocked : function(){
21759         return this.locked;
21760     }
21761 });
21762 /**
21763  * @extends Roo.bootstrap.Table.AbstractSelectionModel
21764  * @class Roo.bootstrap.Table.RowSelectionModel
21765  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
21766  * It supports multiple selections and keyboard selection/navigation. 
21767  * @constructor
21768  * @param {Object} config
21769  */
21770
21771 Roo.bootstrap.Table.RowSelectionModel = function(config){
21772     Roo.apply(this, config);
21773     this.selections = new Roo.util.MixedCollection(false, function(o){
21774         return o.id;
21775     });
21776
21777     this.last = false;
21778     this.lastActive = false;
21779
21780     this.addEvents({
21781         /**
21782              * @event selectionchange
21783              * Fires when the selection changes
21784              * @param {SelectionModel} this
21785              */
21786             "selectionchange" : true,
21787         /**
21788              * @event afterselectionchange
21789              * Fires after the selection changes (eg. by key press or clicking)
21790              * @param {SelectionModel} this
21791              */
21792             "afterselectionchange" : true,
21793         /**
21794              * @event beforerowselect
21795              * Fires when a row is selected being selected, return false to cancel.
21796              * @param {SelectionModel} this
21797              * @param {Number} rowIndex The selected index
21798              * @param {Boolean} keepExisting False if other selections will be cleared
21799              */
21800             "beforerowselect" : true,
21801         /**
21802              * @event rowselect
21803              * Fires when a row is selected.
21804              * @param {SelectionModel} this
21805              * @param {Number} rowIndex The selected index
21806              * @param {Roo.data.Record} r The record
21807              */
21808             "rowselect" : true,
21809         /**
21810              * @event rowdeselect
21811              * Fires when a row is deselected.
21812              * @param {SelectionModel} this
21813              * @param {Number} rowIndex The selected index
21814              */
21815         "rowdeselect" : true
21816     });
21817     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
21818     this.locked = false;
21819 };
21820
21821 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
21822     /**
21823      * @cfg {Boolean} singleSelect
21824      * True to allow selection of only one row at a time (defaults to false)
21825      */
21826     singleSelect : false,
21827
21828     // private
21829     initEvents : function(){
21830
21831         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
21832             this.grid.on("mousedown", this.handleMouseDown, this);
21833         }else{ // allow click to work like normal
21834             this.grid.on("rowclick", this.handleDragableRowClick, this);
21835         }
21836
21837         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
21838             "up" : function(e){
21839                 if(!e.shiftKey){
21840                     this.selectPrevious(e.shiftKey);
21841                 }else if(this.last !== false && this.lastActive !== false){
21842                     var last = this.last;
21843                     this.selectRange(this.last,  this.lastActive-1);
21844                     this.grid.getView().focusRow(this.lastActive);
21845                     if(last !== false){
21846                         this.last = last;
21847                     }
21848                 }else{
21849                     this.selectFirstRow();
21850                 }
21851                 this.fireEvent("afterselectionchange", this);
21852             },
21853             "down" : function(e){
21854                 if(!e.shiftKey){
21855                     this.selectNext(e.shiftKey);
21856                 }else if(this.last !== false && this.lastActive !== false){
21857                     var last = this.last;
21858                     this.selectRange(this.last,  this.lastActive+1);
21859                     this.grid.getView().focusRow(this.lastActive);
21860                     if(last !== false){
21861                         this.last = last;
21862                     }
21863                 }else{
21864                     this.selectFirstRow();
21865                 }
21866                 this.fireEvent("afterselectionchange", this);
21867             },
21868             scope: this
21869         });
21870
21871         var view = this.grid.view;
21872         view.on("refresh", this.onRefresh, this);
21873         view.on("rowupdated", this.onRowUpdated, this);
21874         view.on("rowremoved", this.onRemove, this);
21875     },
21876
21877     // private
21878     onRefresh : function(){
21879         var ds = this.grid.dataSource, i, v = this.grid.view;
21880         var s = this.selections;
21881         s.each(function(r){
21882             if((i = ds.indexOfId(r.id)) != -1){
21883                 v.onRowSelect(i);
21884             }else{
21885                 s.remove(r);
21886             }
21887         });
21888     },
21889
21890     // private
21891     onRemove : function(v, index, r){
21892         this.selections.remove(r);
21893     },
21894
21895     // private
21896     onRowUpdated : function(v, index, r){
21897         if(this.isSelected(r)){
21898             v.onRowSelect(index);
21899         }
21900     },
21901
21902     /**
21903      * Select records.
21904      * @param {Array} records The records to select
21905      * @param {Boolean} keepExisting (optional) True to keep existing selections
21906      */
21907     selectRecords : function(records, keepExisting){
21908         if(!keepExisting){
21909             this.clearSelections();
21910         }
21911         var ds = this.grid.dataSource;
21912         for(var i = 0, len = records.length; i < len; i++){
21913             this.selectRow(ds.indexOf(records[i]), true);
21914         }
21915     },
21916
21917     /**
21918      * Gets the number of selected rows.
21919      * @return {Number}
21920      */
21921     getCount : function(){
21922         return this.selections.length;
21923     },
21924
21925     /**
21926      * Selects the first row in the grid.
21927      */
21928     selectFirstRow : function(){
21929         this.selectRow(0);
21930     },
21931
21932     /**
21933      * Select the last row.
21934      * @param {Boolean} keepExisting (optional) True to keep existing selections
21935      */
21936     selectLastRow : function(keepExisting){
21937         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
21938     },
21939
21940     /**
21941      * Selects the row immediately following the last selected row.
21942      * @param {Boolean} keepExisting (optional) True to keep existing selections
21943      */
21944     selectNext : function(keepExisting){
21945         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
21946             this.selectRow(this.last+1, keepExisting);
21947             this.grid.getView().focusRow(this.last);
21948         }
21949     },
21950
21951     /**
21952      * Selects the row that precedes the last selected row.
21953      * @param {Boolean} keepExisting (optional) True to keep existing selections
21954      */
21955     selectPrevious : function(keepExisting){
21956         if(this.last){
21957             this.selectRow(this.last-1, keepExisting);
21958             this.grid.getView().focusRow(this.last);
21959         }
21960     },
21961
21962     /**
21963      * Returns the selected records
21964      * @return {Array} Array of selected records
21965      */
21966     getSelections : function(){
21967         return [].concat(this.selections.items);
21968     },
21969
21970     /**
21971      * Returns the first selected record.
21972      * @return {Record}
21973      */
21974     getSelected : function(){
21975         return this.selections.itemAt(0);
21976     },
21977
21978
21979     /**
21980      * Clears all selections.
21981      */
21982     clearSelections : function(fast){
21983         if(this.locked) {
21984             return;
21985         }
21986         if(fast !== true){
21987             var ds = this.grid.dataSource;
21988             var s = this.selections;
21989             s.each(function(r){
21990                 this.deselectRow(ds.indexOfId(r.id));
21991             }, this);
21992             s.clear();
21993         }else{
21994             this.selections.clear();
21995         }
21996         this.last = false;
21997     },
21998
21999
22000     /**
22001      * Selects all rows.
22002      */
22003     selectAll : function(){
22004         if(this.locked) {
22005             return;
22006         }
22007         this.selections.clear();
22008         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22009             this.selectRow(i, true);
22010         }
22011     },
22012
22013     /**
22014      * Returns True if there is a selection.
22015      * @return {Boolean}
22016      */
22017     hasSelection : function(){
22018         return this.selections.length > 0;
22019     },
22020
22021     /**
22022      * Returns True if the specified row is selected.
22023      * @param {Number/Record} record The record or index of the record to check
22024      * @return {Boolean}
22025      */
22026     isSelected : function(index){
22027         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22028         return (r && this.selections.key(r.id) ? true : false);
22029     },
22030
22031     /**
22032      * Returns True if the specified record id is selected.
22033      * @param {String} id The id of record to check
22034      * @return {Boolean}
22035      */
22036     isIdSelected : function(id){
22037         return (this.selections.key(id) ? true : false);
22038     },
22039
22040     // private
22041     handleMouseDown : function(e, t){
22042         var view = this.grid.getView(), rowIndex;
22043         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22044             return;
22045         };
22046         if(e.shiftKey && this.last !== false){
22047             var last = this.last;
22048             this.selectRange(last, rowIndex, e.ctrlKey);
22049             this.last = last; // reset the last
22050             view.focusRow(rowIndex);
22051         }else{
22052             var isSelected = this.isSelected(rowIndex);
22053             if(e.button !== 0 && isSelected){
22054                 view.focusRow(rowIndex);
22055             }else if(e.ctrlKey && isSelected){
22056                 this.deselectRow(rowIndex);
22057             }else if(!isSelected){
22058                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22059                 view.focusRow(rowIndex);
22060             }
22061         }
22062         this.fireEvent("afterselectionchange", this);
22063     },
22064     // private
22065     handleDragableRowClick :  function(grid, rowIndex, e) 
22066     {
22067         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22068             this.selectRow(rowIndex, false);
22069             grid.view.focusRow(rowIndex);
22070              this.fireEvent("afterselectionchange", this);
22071         }
22072     },
22073     
22074     /**
22075      * Selects multiple rows.
22076      * @param {Array} rows Array of the indexes of the row to select
22077      * @param {Boolean} keepExisting (optional) True to keep existing selections
22078      */
22079     selectRows : function(rows, keepExisting){
22080         if(!keepExisting){
22081             this.clearSelections();
22082         }
22083         for(var i = 0, len = rows.length; i < len; i++){
22084             this.selectRow(rows[i], true);
22085         }
22086     },
22087
22088     /**
22089      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22090      * @param {Number} startRow The index of the first row in the range
22091      * @param {Number} endRow The index of the last row in the range
22092      * @param {Boolean} keepExisting (optional) True to retain existing selections
22093      */
22094     selectRange : function(startRow, endRow, keepExisting){
22095         if(this.locked) {
22096             return;
22097         }
22098         if(!keepExisting){
22099             this.clearSelections();
22100         }
22101         if(startRow <= endRow){
22102             for(var i = startRow; i <= endRow; i++){
22103                 this.selectRow(i, true);
22104             }
22105         }else{
22106             for(var i = startRow; i >= endRow; i--){
22107                 this.selectRow(i, true);
22108             }
22109         }
22110     },
22111
22112     /**
22113      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22114      * @param {Number} startRow The index of the first row in the range
22115      * @param {Number} endRow The index of the last row in the range
22116      */
22117     deselectRange : function(startRow, endRow, preventViewNotify){
22118         if(this.locked) {
22119             return;
22120         }
22121         for(var i = startRow; i <= endRow; i++){
22122             this.deselectRow(i, preventViewNotify);
22123         }
22124     },
22125
22126     /**
22127      * Selects a row.
22128      * @param {Number} row The index of the row to select
22129      * @param {Boolean} keepExisting (optional) True to keep existing selections
22130      */
22131     selectRow : function(index, keepExisting, preventViewNotify){
22132         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22133             return;
22134         }
22135         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22136             if(!keepExisting || this.singleSelect){
22137                 this.clearSelections();
22138             }
22139             var r = this.grid.dataSource.getAt(index);
22140             this.selections.add(r);
22141             this.last = this.lastActive = index;
22142             if(!preventViewNotify){
22143                 this.grid.getView().onRowSelect(index);
22144             }
22145             this.fireEvent("rowselect", this, index, r);
22146             this.fireEvent("selectionchange", this);
22147         }
22148     },
22149
22150     /**
22151      * Deselects a row.
22152      * @param {Number} row The index of the row to deselect
22153      */
22154     deselectRow : function(index, preventViewNotify){
22155         if(this.locked) {
22156             return;
22157         }
22158         if(this.last == index){
22159             this.last = false;
22160         }
22161         if(this.lastActive == index){
22162             this.lastActive = false;
22163         }
22164         var r = this.grid.dataSource.getAt(index);
22165         this.selections.remove(r);
22166         if(!preventViewNotify){
22167             this.grid.getView().onRowDeselect(index);
22168         }
22169         this.fireEvent("rowdeselect", this, index);
22170         this.fireEvent("selectionchange", this);
22171     },
22172
22173     // private
22174     restoreLast : function(){
22175         if(this._last){
22176             this.last = this._last;
22177         }
22178     },
22179
22180     // private
22181     acceptsNav : function(row, col, cm){
22182         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22183     },
22184
22185     // private
22186     onEditorKey : function(field, e){
22187         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22188         if(k == e.TAB){
22189             e.stopEvent();
22190             ed.completeEdit();
22191             if(e.shiftKey){
22192                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22193             }else{
22194                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22195             }
22196         }else if(k == e.ENTER && !e.ctrlKey){
22197             e.stopEvent();
22198             ed.completeEdit();
22199             if(e.shiftKey){
22200                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22201             }else{
22202                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22203             }
22204         }else if(k == e.ESC){
22205             ed.cancelEdit();
22206         }
22207         if(newCell){
22208             g.startEditing(newCell[0], newCell[1]);
22209         }
22210     }
22211 });/*
22212  * Based on:
22213  * Ext JS Library 1.1.1
22214  * Copyright(c) 2006-2007, Ext JS, LLC.
22215  *
22216  * Originally Released Under LGPL - original licence link has changed is not relivant.
22217  *
22218  * Fork - LGPL
22219  * <script type="text/javascript">
22220  */
22221  
22222 /**
22223  * @class Roo.bootstrap.PagingToolbar
22224  * @extends Roo.bootstrap.NavSimplebar
22225  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22226  * @constructor
22227  * Create a new PagingToolbar
22228  * @param {Object} config The config object
22229  * @param {Roo.data.Store} store
22230  */
22231 Roo.bootstrap.PagingToolbar = function(config)
22232 {
22233     // old args format still supported... - xtype is prefered..
22234         // created from xtype...
22235     
22236     this.ds = config.dataSource;
22237     
22238     if (config.store && !this.ds) {
22239         this.store= Roo.factory(config.store, Roo.data);
22240         this.ds = this.store;
22241         this.ds.xmodule = this.xmodule || false;
22242     }
22243     
22244     this.toolbarItems = [];
22245     if (config.items) {
22246         this.toolbarItems = config.items;
22247     }
22248     
22249     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22250     
22251     this.cursor = 0;
22252     
22253     if (this.ds) { 
22254         this.bind(this.ds);
22255     }
22256     
22257     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22258     
22259 };
22260
22261 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22262     /**
22263      * @cfg {Roo.data.Store} dataSource
22264      * The underlying data store providing the paged data
22265      */
22266     /**
22267      * @cfg {String/HTMLElement/Element} container
22268      * container The id or element that will contain the toolbar
22269      */
22270     /**
22271      * @cfg {Boolean} displayInfo
22272      * True to display the displayMsg (defaults to false)
22273      */
22274     /**
22275      * @cfg {Number} pageSize
22276      * The number of records to display per page (defaults to 20)
22277      */
22278     pageSize: 20,
22279     /**
22280      * @cfg {String} displayMsg
22281      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22282      */
22283     displayMsg : 'Displaying {0} - {1} of {2}',
22284     /**
22285      * @cfg {String} emptyMsg
22286      * The message to display when no records are found (defaults to "No data to display")
22287      */
22288     emptyMsg : 'No data to display',
22289     /**
22290      * Customizable piece of the default paging text (defaults to "Page")
22291      * @type String
22292      */
22293     beforePageText : "Page",
22294     /**
22295      * Customizable piece of the default paging text (defaults to "of %0")
22296      * @type String
22297      */
22298     afterPageText : "of {0}",
22299     /**
22300      * Customizable piece of the default paging text (defaults to "First Page")
22301      * @type String
22302      */
22303     firstText : "First Page",
22304     /**
22305      * Customizable piece of the default paging text (defaults to "Previous Page")
22306      * @type String
22307      */
22308     prevText : "Previous Page",
22309     /**
22310      * Customizable piece of the default paging text (defaults to "Next Page")
22311      * @type String
22312      */
22313     nextText : "Next Page",
22314     /**
22315      * Customizable piece of the default paging text (defaults to "Last Page")
22316      * @type String
22317      */
22318     lastText : "Last Page",
22319     /**
22320      * Customizable piece of the default paging text (defaults to "Refresh")
22321      * @type String
22322      */
22323     refreshText : "Refresh",
22324
22325     buttons : false,
22326     // private
22327     onRender : function(ct, position) 
22328     {
22329         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22330         this.navgroup.parentId = this.id;
22331         this.navgroup.onRender(this.el, null);
22332         // add the buttons to the navgroup
22333         
22334         if(this.displayInfo){
22335             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22336             this.displayEl = this.el.select('.x-paging-info', true).first();
22337 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22338 //            this.displayEl = navel.el.select('span',true).first();
22339         }
22340         
22341         var _this = this;
22342         
22343         if(this.buttons){
22344             Roo.each(_this.buttons, function(e){ // this might need to use render????
22345                Roo.factory(e).onRender(_this.el, null);
22346             });
22347         }
22348             
22349         Roo.each(_this.toolbarItems, function(e) {
22350             _this.navgroup.addItem(e);
22351         });
22352         
22353         
22354         this.first = this.navgroup.addItem({
22355             tooltip: this.firstText,
22356             cls: "prev",
22357             icon : 'fa fa-backward',
22358             disabled: true,
22359             preventDefault: true,
22360             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22361         });
22362         
22363         this.prev =  this.navgroup.addItem({
22364             tooltip: this.prevText,
22365             cls: "prev",
22366             icon : 'fa fa-step-backward',
22367             disabled: true,
22368             preventDefault: true,
22369             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22370         });
22371     //this.addSeparator();
22372         
22373         
22374         var field = this.navgroup.addItem( {
22375             tagtype : 'span',
22376             cls : 'x-paging-position',
22377             
22378             html : this.beforePageText  +
22379                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22380                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22381          } ); //?? escaped?
22382         
22383         this.field = field.el.select('input', true).first();
22384         this.field.on("keydown", this.onPagingKeydown, this);
22385         this.field.on("focus", function(){this.dom.select();});
22386     
22387     
22388         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22389         //this.field.setHeight(18);
22390         //this.addSeparator();
22391         this.next = this.navgroup.addItem({
22392             tooltip: this.nextText,
22393             cls: "next",
22394             html : ' <i class="fa fa-step-forward">',
22395             disabled: true,
22396             preventDefault: true,
22397             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22398         });
22399         this.last = this.navgroup.addItem({
22400             tooltip: this.lastText,
22401             icon : 'fa fa-forward',
22402             cls: "next",
22403             disabled: true,
22404             preventDefault: true,
22405             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22406         });
22407     //this.addSeparator();
22408         this.loading = this.navgroup.addItem({
22409             tooltip: this.refreshText,
22410             icon: 'fa fa-refresh',
22411             preventDefault: true,
22412             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22413         });
22414         
22415     },
22416
22417     // private
22418     updateInfo : function(){
22419         if(this.displayEl){
22420             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22421             var msg = count == 0 ?
22422                 this.emptyMsg :
22423                 String.format(
22424                     this.displayMsg,
22425                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22426                 );
22427             this.displayEl.update(msg);
22428         }
22429     },
22430
22431     // private
22432     onLoad : function(ds, r, o){
22433        this.cursor = o.params ? o.params.start : 0;
22434        var d = this.getPageData(),
22435             ap = d.activePage,
22436             ps = d.pages;
22437         
22438        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22439        this.field.dom.value = ap;
22440        this.first.setDisabled(ap == 1);
22441        this.prev.setDisabled(ap == 1);
22442        this.next.setDisabled(ap == ps);
22443        this.last.setDisabled(ap == ps);
22444        this.loading.enable();
22445        this.updateInfo();
22446     },
22447
22448     // private
22449     getPageData : function(){
22450         var total = this.ds.getTotalCount();
22451         return {
22452             total : total,
22453             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22454             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22455         };
22456     },
22457
22458     // private
22459     onLoadError : function(){
22460         this.loading.enable();
22461     },
22462
22463     // private
22464     onPagingKeydown : function(e){
22465         var k = e.getKey();
22466         var d = this.getPageData();
22467         if(k == e.RETURN){
22468             var v = this.field.dom.value, pageNum;
22469             if(!v || isNaN(pageNum = parseInt(v, 10))){
22470                 this.field.dom.value = d.activePage;
22471                 return;
22472             }
22473             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22474             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22475             e.stopEvent();
22476         }
22477         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
22478         {
22479           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22480           this.field.dom.value = pageNum;
22481           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22482           e.stopEvent();
22483         }
22484         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22485         {
22486           var v = this.field.dom.value, pageNum; 
22487           var increment = (e.shiftKey) ? 10 : 1;
22488           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22489                 increment *= -1;
22490           }
22491           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22492             this.field.dom.value = d.activePage;
22493             return;
22494           }
22495           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22496           {
22497             this.field.dom.value = parseInt(v, 10) + increment;
22498             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22499             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22500           }
22501           e.stopEvent();
22502         }
22503     },
22504
22505     // private
22506     beforeLoad : function(){
22507         if(this.loading){
22508             this.loading.disable();
22509         }
22510     },
22511
22512     // private
22513     onClick : function(which){
22514         
22515         var ds = this.ds;
22516         if (!ds) {
22517             return;
22518         }
22519         
22520         switch(which){
22521             case "first":
22522                 ds.load({params:{start: 0, limit: this.pageSize}});
22523             break;
22524             case "prev":
22525                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22526             break;
22527             case "next":
22528                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22529             break;
22530             case "last":
22531                 var total = ds.getTotalCount();
22532                 var extra = total % this.pageSize;
22533                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22534                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22535             break;
22536             case "refresh":
22537                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22538             break;
22539         }
22540     },
22541
22542     /**
22543      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22544      * @param {Roo.data.Store} store The data store to unbind
22545      */
22546     unbind : function(ds){
22547         ds.un("beforeload", this.beforeLoad, this);
22548         ds.un("load", this.onLoad, this);
22549         ds.un("loadexception", this.onLoadError, this);
22550         ds.un("remove", this.updateInfo, this);
22551         ds.un("add", this.updateInfo, this);
22552         this.ds = undefined;
22553     },
22554
22555     /**
22556      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22557      * @param {Roo.data.Store} store The data store to bind
22558      */
22559     bind : function(ds){
22560         ds.on("beforeload", this.beforeLoad, this);
22561         ds.on("load", this.onLoad, this);
22562         ds.on("loadexception", this.onLoadError, this);
22563         ds.on("remove", this.updateInfo, this);
22564         ds.on("add", this.updateInfo, this);
22565         this.ds = ds;
22566     }
22567 });/*
22568  * - LGPL
22569  *
22570  * element
22571  * 
22572  */
22573
22574 /**
22575  * @class Roo.bootstrap.MessageBar
22576  * @extends Roo.bootstrap.Component
22577  * Bootstrap MessageBar class
22578  * @cfg {String} html contents of the MessageBar
22579  * @cfg {String} weight (info | success | warning | danger) default info
22580  * @cfg {String} beforeClass insert the bar before the given class
22581  * @cfg {Boolean} closable (true | false) default false
22582  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22583  * 
22584  * @constructor
22585  * Create a new Element
22586  * @param {Object} config The config object
22587  */
22588
22589 Roo.bootstrap.MessageBar = function(config){
22590     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22591 };
22592
22593 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22594     
22595     html: '',
22596     weight: 'info',
22597     closable: false,
22598     fixed: false,
22599     beforeClass: 'bootstrap-sticky-wrap',
22600     
22601     getAutoCreate : function(){
22602         
22603         var cfg = {
22604             tag: 'div',
22605             cls: 'alert alert-dismissable alert-' + this.weight,
22606             cn: [
22607                 {
22608                     tag: 'span',
22609                     cls: 'message',
22610                     html: this.html || ''
22611                 }
22612             ]
22613         };
22614         
22615         if(this.fixed){
22616             cfg.cls += ' alert-messages-fixed';
22617         }
22618         
22619         if(this.closable){
22620             cfg.cn.push({
22621                 tag: 'button',
22622                 cls: 'close',
22623                 html: 'x'
22624             });
22625         }
22626         
22627         return cfg;
22628     },
22629     
22630     onRender : function(ct, position)
22631     {
22632         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22633         
22634         if(!this.el){
22635             var cfg = Roo.apply({},  this.getAutoCreate());
22636             cfg.id = Roo.id();
22637             
22638             if (this.cls) {
22639                 cfg.cls += ' ' + this.cls;
22640             }
22641             if (this.style) {
22642                 cfg.style = this.style;
22643             }
22644             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22645             
22646             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22647         }
22648         
22649         this.el.select('>button.close').on('click', this.hide, this);
22650         
22651     },
22652     
22653     show : function()
22654     {
22655         if (!this.rendered) {
22656             this.render();
22657         }
22658         
22659         this.el.show();
22660         
22661         this.fireEvent('show', this);
22662         
22663     },
22664     
22665     hide : function()
22666     {
22667         if (!this.rendered) {
22668             this.render();
22669         }
22670         
22671         this.el.hide();
22672         
22673         this.fireEvent('hide', this);
22674     },
22675     
22676     update : function()
22677     {
22678 //        var e = this.el.dom.firstChild;
22679 //        
22680 //        if(this.closable){
22681 //            e = e.nextSibling;
22682 //        }
22683 //        
22684 //        e.data = this.html || '';
22685
22686         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
22687     }
22688    
22689 });
22690
22691  
22692
22693      /*
22694  * - LGPL
22695  *
22696  * Graph
22697  * 
22698  */
22699
22700
22701 /**
22702  * @class Roo.bootstrap.Graph
22703  * @extends Roo.bootstrap.Component
22704  * Bootstrap Graph class
22705 > Prameters
22706  -sm {number} sm 4
22707  -md {number} md 5
22708  @cfg {String} graphtype  bar | vbar | pie
22709  @cfg {number} g_x coodinator | centre x (pie)
22710  @cfg {number} g_y coodinator | centre y (pie)
22711  @cfg {number} g_r radius (pie)
22712  @cfg {number} g_height height of the chart (respected by all elements in the set)
22713  @cfg {number} g_width width of the chart (respected by all elements in the set)
22714  @cfg {Object} title The title of the chart
22715     
22716  -{Array}  values
22717  -opts (object) options for the chart 
22718      o {
22719      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
22720      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
22721      o vgutter (number)
22722      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
22723      o stacked (boolean) whether or not to tread values as in a stacked bar chart
22724      o to
22725      o stretch (boolean)
22726      o }
22727  -opts (object) options for the pie
22728      o{
22729      o cut
22730      o startAngle (number)
22731      o endAngle (number)
22732      } 
22733  *
22734  * @constructor
22735  * Create a new Input
22736  * @param {Object} config The config object
22737  */
22738
22739 Roo.bootstrap.Graph = function(config){
22740     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
22741     
22742     this.addEvents({
22743         // img events
22744         /**
22745          * @event click
22746          * The img click event for the img.
22747          * @param {Roo.EventObject} e
22748          */
22749         "click" : true
22750     });
22751 };
22752
22753 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
22754     
22755     sm: 4,
22756     md: 5,
22757     graphtype: 'bar',
22758     g_height: 250,
22759     g_width: 400,
22760     g_x: 50,
22761     g_y: 50,
22762     g_r: 30,
22763     opts:{
22764         //g_colors: this.colors,
22765         g_type: 'soft',
22766         g_gutter: '20%'
22767
22768     },
22769     title : false,
22770
22771     getAutoCreate : function(){
22772         
22773         var cfg = {
22774             tag: 'div',
22775             html : null
22776         };
22777         
22778         
22779         return  cfg;
22780     },
22781
22782     onRender : function(ct,position){
22783         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
22784         this.raphael = Raphael(this.el.dom);
22785         
22786                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22787                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22788                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
22789                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
22790                 /*
22791                 r.text(160, 10, "Single Series Chart").attr(txtattr);
22792                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
22793                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
22794                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
22795                 
22796                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
22797                 r.barchart(330, 10, 300, 220, data1);
22798                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
22799                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
22800                 */
22801                 
22802                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22803                 // r.barchart(30, 30, 560, 250,  xdata, {
22804                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
22805                 //     axis : "0 0 1 1",
22806                 //     axisxlabels :  xdata
22807                 //     //yvalues : cols,
22808                    
22809                 // });
22810 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
22811 //        
22812 //        this.load(null,xdata,{
22813 //                axis : "0 0 1 1",
22814 //                axisxlabels :  xdata
22815 //                });
22816
22817     },
22818
22819     load : function(graphtype,xdata,opts){
22820         this.raphael.clear();
22821         if(!graphtype) {
22822             graphtype = this.graphtype;
22823         }
22824         if(!opts){
22825             opts = this.opts;
22826         }
22827         var r = this.raphael,
22828             fin = function () {
22829                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
22830             },
22831             fout = function () {
22832                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
22833             },
22834             pfin = function() {
22835                 this.sector.stop();
22836                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
22837
22838                 if (this.label) {
22839                     this.label[0].stop();
22840                     this.label[0].attr({ r: 7.5 });
22841                     this.label[1].attr({ "font-weight": 800 });
22842                 }
22843             },
22844             pfout = function() {
22845                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
22846
22847                 if (this.label) {
22848                     this.label[0].animate({ r: 5 }, 500, "bounce");
22849                     this.label[1].attr({ "font-weight": 400 });
22850                 }
22851             };
22852
22853         switch(graphtype){
22854             case 'bar':
22855                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22856                 break;
22857             case 'hbar':
22858                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
22859                 break;
22860             case 'pie':
22861 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
22862 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
22863 //            
22864                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
22865                 
22866                 break;
22867
22868         }
22869         
22870         if(this.title){
22871             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
22872         }
22873         
22874     },
22875     
22876     setTitle: function(o)
22877     {
22878         this.title = o;
22879     },
22880     
22881     initEvents: function() {
22882         
22883         if(!this.href){
22884             this.el.on('click', this.onClick, this);
22885         }
22886     },
22887     
22888     onClick : function(e)
22889     {
22890         Roo.log('img onclick');
22891         this.fireEvent('click', this, e);
22892     }
22893    
22894 });
22895
22896  
22897 /*
22898  * - LGPL
22899  *
22900  * numberBox
22901  * 
22902  */
22903 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
22904
22905 /**
22906  * @class Roo.bootstrap.dash.NumberBox
22907  * @extends Roo.bootstrap.Component
22908  * Bootstrap NumberBox class
22909  * @cfg {String} headline Box headline
22910  * @cfg {String} content Box content
22911  * @cfg {String} icon Box icon
22912  * @cfg {String} footer Footer text
22913  * @cfg {String} fhref Footer href
22914  * 
22915  * @constructor
22916  * Create a new NumberBox
22917  * @param {Object} config The config object
22918  */
22919
22920
22921 Roo.bootstrap.dash.NumberBox = function(config){
22922     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
22923     
22924 };
22925
22926 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
22927     
22928     headline : '',
22929     content : '',
22930     icon : '',
22931     footer : '',
22932     fhref : '',
22933     ficon : '',
22934     
22935     getAutoCreate : function(){
22936         
22937         var cfg = {
22938             tag : 'div',
22939             cls : 'small-box ',
22940             cn : [
22941                 {
22942                     tag : 'div',
22943                     cls : 'inner',
22944                     cn :[
22945                         {
22946                             tag : 'h3',
22947                             cls : 'roo-headline',
22948                             html : this.headline
22949                         },
22950                         {
22951                             tag : 'p',
22952                             cls : 'roo-content',
22953                             html : this.content
22954                         }
22955                     ]
22956                 }
22957             ]
22958         };
22959         
22960         if(this.icon){
22961             cfg.cn.push({
22962                 tag : 'div',
22963                 cls : 'icon',
22964                 cn :[
22965                     {
22966                         tag : 'i',
22967                         cls : 'ion ' + this.icon
22968                     }
22969                 ]
22970             });
22971         }
22972         
22973         if(this.footer){
22974             var footer = {
22975                 tag : 'a',
22976                 cls : 'small-box-footer',
22977                 href : this.fhref || '#',
22978                 html : this.footer
22979             };
22980             
22981             cfg.cn.push(footer);
22982             
22983         }
22984         
22985         return  cfg;
22986     },
22987
22988     onRender : function(ct,position){
22989         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
22990
22991
22992        
22993                 
22994     },
22995
22996     setHeadline: function (value)
22997     {
22998         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
22999     },
23000     
23001     setFooter: function (value, href)
23002     {
23003         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23004         
23005         if(href){
23006             this.el.select('a.small-box-footer',true).first().attr('href', href);
23007         }
23008         
23009     },
23010
23011     setContent: function (value)
23012     {
23013         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23014     },
23015
23016     initEvents: function() 
23017     {   
23018         
23019     }
23020     
23021 });
23022
23023  
23024 /*
23025  * - LGPL
23026  *
23027  * TabBox
23028  * 
23029  */
23030 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23031
23032 /**
23033  * @class Roo.bootstrap.dash.TabBox
23034  * @extends Roo.bootstrap.Component
23035  * Bootstrap TabBox class
23036  * @cfg {String} title Title of the TabBox
23037  * @cfg {String} icon Icon of the TabBox
23038  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23039  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23040  * 
23041  * @constructor
23042  * Create a new TabBox
23043  * @param {Object} config The config object
23044  */
23045
23046
23047 Roo.bootstrap.dash.TabBox = function(config){
23048     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23049     this.addEvents({
23050         // raw events
23051         /**
23052          * @event addpane
23053          * When a pane is added
23054          * @param {Roo.bootstrap.dash.TabPane} pane
23055          */
23056         "addpane" : true,
23057         /**
23058          * @event activatepane
23059          * When a pane is activated
23060          * @param {Roo.bootstrap.dash.TabPane} pane
23061          */
23062         "activatepane" : true
23063         
23064          
23065     });
23066     
23067     this.panes = [];
23068 };
23069
23070 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23071
23072     title : '',
23073     icon : false,
23074     showtabs : true,
23075     tabScrollable : false,
23076     
23077     getChildContainer : function()
23078     {
23079         return this.el.select('.tab-content', true).first();
23080     },
23081     
23082     getAutoCreate : function(){
23083         
23084         var header = {
23085             tag: 'li',
23086             cls: 'pull-left header',
23087             html: this.title,
23088             cn : []
23089         };
23090         
23091         if(this.icon){
23092             header.cn.push({
23093                 tag: 'i',
23094                 cls: 'fa ' + this.icon
23095             });
23096         }
23097         
23098         var h = {
23099             tag: 'ul',
23100             cls: 'nav nav-tabs pull-right',
23101             cn: [
23102                 header
23103             ]
23104         };
23105         
23106         if(this.tabScrollable){
23107             h = {
23108                 tag: 'div',
23109                 cls: 'tab-header',
23110                 cn: [
23111                     {
23112                         tag: 'ul',
23113                         cls: 'nav nav-tabs pull-right',
23114                         cn: [
23115                             header
23116                         ]
23117                     }
23118                 ]
23119             };
23120         }
23121         
23122         var cfg = {
23123             tag: 'div',
23124             cls: 'nav-tabs-custom',
23125             cn: [
23126                 h,
23127                 {
23128                     tag: 'div',
23129                     cls: 'tab-content no-padding',
23130                     cn: []
23131                 }
23132             ]
23133         };
23134
23135         return  cfg;
23136     },
23137     initEvents : function()
23138     {
23139         //Roo.log('add add pane handler');
23140         this.on('addpane', this.onAddPane, this);
23141     },
23142      /**
23143      * Updates the box title
23144      * @param {String} html to set the title to.
23145      */
23146     setTitle : function(value)
23147     {
23148         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23149     },
23150     onAddPane : function(pane)
23151     {
23152         this.panes.push(pane);
23153         //Roo.log('addpane');
23154         //Roo.log(pane);
23155         // tabs are rendere left to right..
23156         if(!this.showtabs){
23157             return;
23158         }
23159         
23160         var ctr = this.el.select('.nav-tabs', true).first();
23161          
23162          
23163         var existing = ctr.select('.nav-tab',true);
23164         var qty = existing.getCount();;
23165         
23166         
23167         var tab = ctr.createChild({
23168             tag : 'li',
23169             cls : 'nav-tab' + (qty ? '' : ' active'),
23170             cn : [
23171                 {
23172                     tag : 'a',
23173                     href:'#',
23174                     html : pane.title
23175                 }
23176             ]
23177         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23178         pane.tab = tab;
23179         
23180         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23181         if (!qty) {
23182             pane.el.addClass('active');
23183         }
23184         
23185                 
23186     },
23187     onTabClick : function(ev,un,ob,pane)
23188     {
23189         //Roo.log('tab - prev default');
23190         ev.preventDefault();
23191         
23192         
23193         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23194         pane.tab.addClass('active');
23195         //Roo.log(pane.title);
23196         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23197         // technically we should have a deactivate event.. but maybe add later.
23198         // and it should not de-activate the selected tab...
23199         this.fireEvent('activatepane', pane);
23200         pane.el.addClass('active');
23201         pane.fireEvent('activate');
23202         
23203         
23204     },
23205     
23206     getActivePane : function()
23207     {
23208         var r = false;
23209         Roo.each(this.panes, function(p) {
23210             if(p.el.hasClass('active')){
23211                 r = p;
23212                 return false;
23213             }
23214             
23215             return;
23216         });
23217         
23218         return r;
23219     }
23220     
23221     
23222 });
23223
23224  
23225 /*
23226  * - LGPL
23227  *
23228  * Tab pane
23229  * 
23230  */
23231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23232 /**
23233  * @class Roo.bootstrap.TabPane
23234  * @extends Roo.bootstrap.Component
23235  * Bootstrap TabPane class
23236  * @cfg {Boolean} active (false | true) Default false
23237  * @cfg {String} title title of panel
23238
23239  * 
23240  * @constructor
23241  * Create a new TabPane
23242  * @param {Object} config The config object
23243  */
23244
23245 Roo.bootstrap.dash.TabPane = function(config){
23246     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23247     
23248     this.addEvents({
23249         // raw events
23250         /**
23251          * @event activate
23252          * When a pane is activated
23253          * @param {Roo.bootstrap.dash.TabPane} pane
23254          */
23255         "activate" : true
23256          
23257     });
23258 };
23259
23260 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23261     
23262     active : false,
23263     title : '',
23264     
23265     // the tabBox that this is attached to.
23266     tab : false,
23267      
23268     getAutoCreate : function() 
23269     {
23270         var cfg = {
23271             tag: 'div',
23272             cls: 'tab-pane'
23273         };
23274         
23275         if(this.active){
23276             cfg.cls += ' active';
23277         }
23278         
23279         return cfg;
23280     },
23281     initEvents  : function()
23282     {
23283         //Roo.log('trigger add pane handler');
23284         this.parent().fireEvent('addpane', this)
23285     },
23286     
23287      /**
23288      * Updates the tab title 
23289      * @param {String} html to set the title to.
23290      */
23291     setTitle: function(str)
23292     {
23293         if (!this.tab) {
23294             return;
23295         }
23296         this.title = str;
23297         this.tab.select('a', true).first().dom.innerHTML = str;
23298         
23299     }
23300     
23301     
23302     
23303 });
23304
23305  
23306
23307
23308  /*
23309  * - LGPL
23310  *
23311  * menu
23312  * 
23313  */
23314 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23315
23316 /**
23317  * @class Roo.bootstrap.menu.Menu
23318  * @extends Roo.bootstrap.Component
23319  * Bootstrap Menu class - container for Menu
23320  * @cfg {String} html Text of the menu
23321  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23322  * @cfg {String} icon Font awesome icon
23323  * @cfg {String} pos Menu align to (top | bottom) default bottom
23324  * 
23325  * 
23326  * @constructor
23327  * Create a new Menu
23328  * @param {Object} config The config object
23329  */
23330
23331
23332 Roo.bootstrap.menu.Menu = function(config){
23333     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23334     
23335     this.addEvents({
23336         /**
23337          * @event beforeshow
23338          * Fires before this menu is displayed
23339          * @param {Roo.bootstrap.menu.Menu} this
23340          */
23341         beforeshow : true,
23342         /**
23343          * @event beforehide
23344          * Fires before this menu is hidden
23345          * @param {Roo.bootstrap.menu.Menu} this
23346          */
23347         beforehide : true,
23348         /**
23349          * @event show
23350          * Fires after this menu is displayed
23351          * @param {Roo.bootstrap.menu.Menu} this
23352          */
23353         show : true,
23354         /**
23355          * @event hide
23356          * Fires after this menu is hidden
23357          * @param {Roo.bootstrap.menu.Menu} this
23358          */
23359         hide : true,
23360         /**
23361          * @event click
23362          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23363          * @param {Roo.bootstrap.menu.Menu} this
23364          * @param {Roo.EventObject} e
23365          */
23366         click : true
23367     });
23368     
23369 };
23370
23371 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23372     
23373     submenu : false,
23374     html : '',
23375     weight : 'default',
23376     icon : false,
23377     pos : 'bottom',
23378     
23379     
23380     getChildContainer : function() {
23381         if(this.isSubMenu){
23382             return this.el;
23383         }
23384         
23385         return this.el.select('ul.dropdown-menu', true).first();  
23386     },
23387     
23388     getAutoCreate : function()
23389     {
23390         var text = [
23391             {
23392                 tag : 'span',
23393                 cls : 'roo-menu-text',
23394                 html : this.html
23395             }
23396         ];
23397         
23398         if(this.icon){
23399             text.unshift({
23400                 tag : 'i',
23401                 cls : 'fa ' + this.icon
23402             })
23403         }
23404         
23405         
23406         var cfg = {
23407             tag : 'div',
23408             cls : 'btn-group',
23409             cn : [
23410                 {
23411                     tag : 'button',
23412                     cls : 'dropdown-button btn btn-' + this.weight,
23413                     cn : text
23414                 },
23415                 {
23416                     tag : 'button',
23417                     cls : 'dropdown-toggle btn btn-' + this.weight,
23418                     cn : [
23419                         {
23420                             tag : 'span',
23421                             cls : 'caret'
23422                         }
23423                     ]
23424                 },
23425                 {
23426                     tag : 'ul',
23427                     cls : 'dropdown-menu'
23428                 }
23429             ]
23430             
23431         };
23432         
23433         if(this.pos == 'top'){
23434             cfg.cls += ' dropup';
23435         }
23436         
23437         if(this.isSubMenu){
23438             cfg = {
23439                 tag : 'ul',
23440                 cls : 'dropdown-menu'
23441             }
23442         }
23443         
23444         return cfg;
23445     },
23446     
23447     onRender : function(ct, position)
23448     {
23449         this.isSubMenu = ct.hasClass('dropdown-submenu');
23450         
23451         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23452     },
23453     
23454     initEvents : function() 
23455     {
23456         if(this.isSubMenu){
23457             return;
23458         }
23459         
23460         this.hidden = true;
23461         
23462         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23463         this.triggerEl.on('click', this.onTriggerPress, this);
23464         
23465         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23466         this.buttonEl.on('click', this.onClick, this);
23467         
23468     },
23469     
23470     list : function()
23471     {
23472         if(this.isSubMenu){
23473             return this.el;
23474         }
23475         
23476         return this.el.select('ul.dropdown-menu', true).first();
23477     },
23478     
23479     onClick : function(e)
23480     {
23481         this.fireEvent("click", this, e);
23482     },
23483     
23484     onTriggerPress  : function(e)
23485     {   
23486         if (this.isVisible()) {
23487             this.hide();
23488         } else {
23489             this.show();
23490         }
23491     },
23492     
23493     isVisible : function(){
23494         return !this.hidden;
23495     },
23496     
23497     show : function()
23498     {
23499         this.fireEvent("beforeshow", this);
23500         
23501         this.hidden = false;
23502         this.el.addClass('open');
23503         
23504         Roo.get(document).on("mouseup", this.onMouseUp, this);
23505         
23506         this.fireEvent("show", this);
23507         
23508         
23509     },
23510     
23511     hide : function()
23512     {
23513         this.fireEvent("beforehide", this);
23514         
23515         this.hidden = true;
23516         this.el.removeClass('open');
23517         
23518         Roo.get(document).un("mouseup", this.onMouseUp);
23519         
23520         this.fireEvent("hide", this);
23521     },
23522     
23523     onMouseUp : function()
23524     {
23525         this.hide();
23526     }
23527     
23528 });
23529
23530  
23531  /*
23532  * - LGPL
23533  *
23534  * menu item
23535  * 
23536  */
23537 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23538
23539 /**
23540  * @class Roo.bootstrap.menu.Item
23541  * @extends Roo.bootstrap.Component
23542  * Bootstrap MenuItem class
23543  * @cfg {Boolean} submenu (true | false) default false
23544  * @cfg {String} html text of the item
23545  * @cfg {String} href the link
23546  * @cfg {Boolean} disable (true | false) default false
23547  * @cfg {Boolean} preventDefault (true | false) default true
23548  * @cfg {String} icon Font awesome icon
23549  * @cfg {String} pos Submenu align to (left | right) default right 
23550  * 
23551  * 
23552  * @constructor
23553  * Create a new Item
23554  * @param {Object} config The config object
23555  */
23556
23557
23558 Roo.bootstrap.menu.Item = function(config){
23559     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23560     this.addEvents({
23561         /**
23562          * @event mouseover
23563          * Fires when the mouse is hovering over this menu
23564          * @param {Roo.bootstrap.menu.Item} this
23565          * @param {Roo.EventObject} e
23566          */
23567         mouseover : true,
23568         /**
23569          * @event mouseout
23570          * Fires when the mouse exits this menu
23571          * @param {Roo.bootstrap.menu.Item} this
23572          * @param {Roo.EventObject} e
23573          */
23574         mouseout : true,
23575         // raw events
23576         /**
23577          * @event click
23578          * The raw click event for the entire grid.
23579          * @param {Roo.EventObject} e
23580          */
23581         click : true
23582     });
23583 };
23584
23585 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23586     
23587     submenu : false,
23588     href : '',
23589     html : '',
23590     preventDefault: true,
23591     disable : false,
23592     icon : false,
23593     pos : 'right',
23594     
23595     getAutoCreate : function()
23596     {
23597         var text = [
23598             {
23599                 tag : 'span',
23600                 cls : 'roo-menu-item-text',
23601                 html : this.html
23602             }
23603         ];
23604         
23605         if(this.icon){
23606             text.unshift({
23607                 tag : 'i',
23608                 cls : 'fa ' + this.icon
23609             })
23610         }
23611         
23612         var cfg = {
23613             tag : 'li',
23614             cn : [
23615                 {
23616                     tag : 'a',
23617                     href : this.href || '#',
23618                     cn : text
23619                 }
23620             ]
23621         };
23622         
23623         if(this.disable){
23624             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23625         }
23626         
23627         if(this.submenu){
23628             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23629             
23630             if(this.pos == 'left'){
23631                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23632             }
23633         }
23634         
23635         return cfg;
23636     },
23637     
23638     initEvents : function() 
23639     {
23640         this.el.on('mouseover', this.onMouseOver, this);
23641         this.el.on('mouseout', this.onMouseOut, this);
23642         
23643         this.el.select('a', true).first().on('click', this.onClick, this);
23644         
23645     },
23646     
23647     onClick : function(e)
23648     {
23649         if(this.preventDefault){
23650             e.preventDefault();
23651         }
23652         
23653         this.fireEvent("click", this, e);
23654     },
23655     
23656     onMouseOver : function(e)
23657     {
23658         if(this.submenu && this.pos == 'left'){
23659             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23660         }
23661         
23662         this.fireEvent("mouseover", this, e);
23663     },
23664     
23665     onMouseOut : function(e)
23666     {
23667         this.fireEvent("mouseout", this, e);
23668     }
23669 });
23670
23671  
23672
23673  /*
23674  * - LGPL
23675  *
23676  * menu separator
23677  * 
23678  */
23679 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23680
23681 /**
23682  * @class Roo.bootstrap.menu.Separator
23683  * @extends Roo.bootstrap.Component
23684  * Bootstrap Separator class
23685  * 
23686  * @constructor
23687  * Create a new Separator
23688  * @param {Object} config The config object
23689  */
23690
23691
23692 Roo.bootstrap.menu.Separator = function(config){
23693     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
23694 };
23695
23696 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
23697     
23698     getAutoCreate : function(){
23699         var cfg = {
23700             tag : 'li',
23701             cls: 'divider'
23702         };
23703         
23704         return cfg;
23705     }
23706    
23707 });
23708
23709  
23710
23711  /*
23712  * - LGPL
23713  *
23714  * Tooltip
23715  * 
23716  */
23717
23718 /**
23719  * @class Roo.bootstrap.Tooltip
23720  * Bootstrap Tooltip class
23721  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
23722  * to determine which dom element triggers the tooltip.
23723  * 
23724  * It needs to add support for additional attributes like tooltip-position
23725  * 
23726  * @constructor
23727  * Create a new Toolti
23728  * @param {Object} config The config object
23729  */
23730
23731 Roo.bootstrap.Tooltip = function(config){
23732     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
23733 };
23734
23735 Roo.apply(Roo.bootstrap.Tooltip, {
23736     /**
23737      * @function init initialize tooltip monitoring.
23738      * @static
23739      */
23740     currentEl : false,
23741     currentTip : false,
23742     currentRegion : false,
23743     
23744     //  init : delay?
23745     
23746     init : function()
23747     {
23748         Roo.get(document).on('mouseover', this.enter ,this);
23749         Roo.get(document).on('mouseout', this.leave, this);
23750          
23751         
23752         this.currentTip = new Roo.bootstrap.Tooltip();
23753     },
23754     
23755     enter : function(ev)
23756     {
23757         var dom = ev.getTarget();
23758         
23759         //Roo.log(['enter',dom]);
23760         var el = Roo.fly(dom);
23761         if (this.currentEl) {
23762             //Roo.log(dom);
23763             //Roo.log(this.currentEl);
23764             //Roo.log(this.currentEl.contains(dom));
23765             if (this.currentEl == el) {
23766                 return;
23767             }
23768             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
23769                 return;
23770             }
23771
23772         }
23773         
23774         if (this.currentTip.el) {
23775             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
23776         }    
23777         //Roo.log(ev);
23778         var bindEl = el;
23779         
23780         // you can not look for children, as if el is the body.. then everythign is the child..
23781         if (!el.attr('tooltip')) { //
23782             if (!el.select("[tooltip]").elements.length) {
23783                 return;
23784             }
23785             // is the mouse over this child...?
23786             bindEl = el.select("[tooltip]").first();
23787             var xy = ev.getXY();
23788             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
23789                 //Roo.log("not in region.");
23790                 return;
23791             }
23792             //Roo.log("child element over..");
23793             
23794         }
23795         this.currentEl = bindEl;
23796         this.currentTip.bind(bindEl);
23797         this.currentRegion = Roo.lib.Region.getRegion(dom);
23798         this.currentTip.enter();
23799         
23800     },
23801     leave : function(ev)
23802     {
23803         var dom = ev.getTarget();
23804         //Roo.log(['leave',dom]);
23805         if (!this.currentEl) {
23806             return;
23807         }
23808         
23809         
23810         if (dom != this.currentEl.dom) {
23811             return;
23812         }
23813         var xy = ev.getXY();
23814         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
23815             return;
23816         }
23817         // only activate leave if mouse cursor is outside... bounding box..
23818         
23819         
23820         
23821         
23822         if (this.currentTip) {
23823             this.currentTip.leave();
23824         }
23825         //Roo.log('clear currentEl');
23826         this.currentEl = false;
23827         
23828         
23829     },
23830     alignment : {
23831         'left' : ['r-l', [-2,0], 'right'],
23832         'right' : ['l-r', [2,0], 'left'],
23833         'bottom' : ['t-b', [0,2], 'top'],
23834         'top' : [ 'b-t', [0,-2], 'bottom']
23835     }
23836     
23837 });
23838
23839
23840 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
23841     
23842     
23843     bindEl : false,
23844     
23845     delay : null, // can be { show : 300 , hide: 500}
23846     
23847     timeout : null,
23848     
23849     hoverState : null, //???
23850     
23851     placement : 'bottom', 
23852     
23853     getAutoCreate : function(){
23854     
23855         var cfg = {
23856            cls : 'tooltip',
23857            role : 'tooltip',
23858            cn : [
23859                 {
23860                     cls : 'tooltip-arrow'
23861                 },
23862                 {
23863                     cls : 'tooltip-inner'
23864                 }
23865            ]
23866         };
23867         
23868         return cfg;
23869     },
23870     bind : function(el)
23871     {
23872         this.bindEl = el;
23873     },
23874       
23875     
23876     enter : function () {
23877        
23878         if (this.timeout != null) {
23879             clearTimeout(this.timeout);
23880         }
23881         
23882         this.hoverState = 'in';
23883          //Roo.log("enter - show");
23884         if (!this.delay || !this.delay.show) {
23885             this.show();
23886             return;
23887         }
23888         var _t = this;
23889         this.timeout = setTimeout(function () {
23890             if (_t.hoverState == 'in') {
23891                 _t.show();
23892             }
23893         }, this.delay.show);
23894     },
23895     leave : function()
23896     {
23897         clearTimeout(this.timeout);
23898     
23899         this.hoverState = 'out';
23900          if (!this.delay || !this.delay.hide) {
23901             this.hide();
23902             return;
23903         }
23904        
23905         var _t = this;
23906         this.timeout = setTimeout(function () {
23907             //Roo.log("leave - timeout");
23908             
23909             if (_t.hoverState == 'out') {
23910                 _t.hide();
23911                 Roo.bootstrap.Tooltip.currentEl = false;
23912             }
23913         }, delay);
23914     },
23915     
23916     show : function ()
23917     {
23918         if (!this.el) {
23919             this.render(document.body);
23920         }
23921         // set content.
23922         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
23923         
23924         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
23925         
23926         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
23927         
23928         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
23929         
23930         var placement = typeof this.placement == 'function' ?
23931             this.placement.call(this, this.el, on_el) :
23932             this.placement;
23933             
23934         var autoToken = /\s?auto?\s?/i;
23935         var autoPlace = autoToken.test(placement);
23936         if (autoPlace) {
23937             placement = placement.replace(autoToken, '') || 'top';
23938         }
23939         
23940         //this.el.detach()
23941         //this.el.setXY([0,0]);
23942         this.el.show();
23943         //this.el.dom.style.display='block';
23944         
23945         //this.el.appendTo(on_el);
23946         
23947         var p = this.getPosition();
23948         var box = this.el.getBox();
23949         
23950         if (autoPlace) {
23951             // fixme..
23952         }
23953         
23954         var align = Roo.bootstrap.Tooltip.alignment[placement];
23955         
23956         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
23957         
23958         if(placement == 'top' || placement == 'bottom'){
23959             if(xy[0] < 0){
23960                 placement = 'right';
23961             }
23962             
23963             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
23964                 placement = 'left';
23965             }
23966         }
23967         
23968         align = Roo.bootstrap.Tooltip.alignment[placement];
23969         
23970         this.el.alignTo(this.bindEl, align[0],align[1]);
23971         //var arrow = this.el.select('.arrow',true).first();
23972         //arrow.set(align[2], 
23973         
23974         this.el.addClass(placement);
23975         
23976         this.el.addClass('in fade');
23977         
23978         this.hoverState = null;
23979         
23980         if (this.el.hasClass('fade')) {
23981             // fade it?
23982         }
23983         
23984     },
23985     hide : function()
23986     {
23987          
23988         if (!this.el) {
23989             return;
23990         }
23991         //this.el.setXY([0,0]);
23992         this.el.removeClass('in');
23993         //this.el.hide();
23994         
23995     }
23996     
23997 });
23998  
23999
24000  /*
24001  * - LGPL
24002  *
24003  * Location Picker
24004  * 
24005  */
24006
24007 /**
24008  * @class Roo.bootstrap.LocationPicker
24009  * @extends Roo.bootstrap.Component
24010  * Bootstrap LocationPicker class
24011  * @cfg {Number} latitude Position when init default 0
24012  * @cfg {Number} longitude Position when init default 0
24013  * @cfg {Number} zoom default 15
24014  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24015  * @cfg {Boolean} mapTypeControl default false
24016  * @cfg {Boolean} disableDoubleClickZoom default false
24017  * @cfg {Boolean} scrollwheel default true
24018  * @cfg {Boolean} streetViewControl default false
24019  * @cfg {Number} radius default 0
24020  * @cfg {String} locationName
24021  * @cfg {Boolean} draggable default true
24022  * @cfg {Boolean} enableAutocomplete default false
24023  * @cfg {Boolean} enableReverseGeocode default true
24024  * @cfg {String} markerTitle
24025  * 
24026  * @constructor
24027  * Create a new LocationPicker
24028  * @param {Object} config The config object
24029  */
24030
24031
24032 Roo.bootstrap.LocationPicker = function(config){
24033     
24034     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24035     
24036     this.addEvents({
24037         /**
24038          * @event initial
24039          * Fires when the picker initialized.
24040          * @param {Roo.bootstrap.LocationPicker} this
24041          * @param {Google Location} location
24042          */
24043         initial : true,
24044         /**
24045          * @event positionchanged
24046          * Fires when the picker position changed.
24047          * @param {Roo.bootstrap.LocationPicker} this
24048          * @param {Google Location} location
24049          */
24050         positionchanged : true,
24051         /**
24052          * @event resize
24053          * Fires when the map resize.
24054          * @param {Roo.bootstrap.LocationPicker} this
24055          */
24056         resize : true,
24057         /**
24058          * @event show
24059          * Fires when the map show.
24060          * @param {Roo.bootstrap.LocationPicker} this
24061          */
24062         show : true,
24063         /**
24064          * @event hide
24065          * Fires when the map hide.
24066          * @param {Roo.bootstrap.LocationPicker} this
24067          */
24068         hide : true,
24069         /**
24070          * @event mapClick
24071          * Fires when click the map.
24072          * @param {Roo.bootstrap.LocationPicker} this
24073          * @param {Map event} e
24074          */
24075         mapClick : true,
24076         /**
24077          * @event mapRightClick
24078          * Fires when right click the map.
24079          * @param {Roo.bootstrap.LocationPicker} this
24080          * @param {Map event} e
24081          */
24082         mapRightClick : true,
24083         /**
24084          * @event markerClick
24085          * Fires when click the marker.
24086          * @param {Roo.bootstrap.LocationPicker} this
24087          * @param {Map event} e
24088          */
24089         markerClick : true,
24090         /**
24091          * @event markerRightClick
24092          * Fires when right click the marker.
24093          * @param {Roo.bootstrap.LocationPicker} this
24094          * @param {Map event} e
24095          */
24096         markerRightClick : true,
24097         /**
24098          * @event OverlayViewDraw
24099          * Fires when OverlayView Draw
24100          * @param {Roo.bootstrap.LocationPicker} this
24101          */
24102         OverlayViewDraw : true,
24103         /**
24104          * @event OverlayViewOnAdd
24105          * Fires when OverlayView Draw
24106          * @param {Roo.bootstrap.LocationPicker} this
24107          */
24108         OverlayViewOnAdd : true,
24109         /**
24110          * @event OverlayViewOnRemove
24111          * Fires when OverlayView Draw
24112          * @param {Roo.bootstrap.LocationPicker} this
24113          */
24114         OverlayViewOnRemove : true,
24115         /**
24116          * @event OverlayViewShow
24117          * Fires when OverlayView Draw
24118          * @param {Roo.bootstrap.LocationPicker} this
24119          * @param {Pixel} cpx
24120          */
24121         OverlayViewShow : true,
24122         /**
24123          * @event OverlayViewHide
24124          * Fires when OverlayView Draw
24125          * @param {Roo.bootstrap.LocationPicker} this
24126          */
24127         OverlayViewHide : true,
24128         /**
24129          * @event loadexception
24130          * Fires when load google lib failed.
24131          * @param {Roo.bootstrap.LocationPicker} this
24132          */
24133         loadexception : true
24134     });
24135         
24136 };
24137
24138 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24139     
24140     gMapContext: false,
24141     
24142     latitude: 0,
24143     longitude: 0,
24144     zoom: 15,
24145     mapTypeId: false,
24146     mapTypeControl: false,
24147     disableDoubleClickZoom: false,
24148     scrollwheel: true,
24149     streetViewControl: false,
24150     radius: 0,
24151     locationName: '',
24152     draggable: true,
24153     enableAutocomplete: false,
24154     enableReverseGeocode: true,
24155     markerTitle: '',
24156     
24157     getAutoCreate: function()
24158     {
24159
24160         var cfg = {
24161             tag: 'div',
24162             cls: 'roo-location-picker'
24163         };
24164         
24165         return cfg
24166     },
24167     
24168     initEvents: function(ct, position)
24169     {       
24170         if(!this.el.getWidth() || this.isApplied()){
24171             return;
24172         }
24173         
24174         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24175         
24176         this.initial();
24177     },
24178     
24179     initial: function()
24180     {
24181         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24182             this.fireEvent('loadexception', this);
24183             return;
24184         }
24185         
24186         if(!this.mapTypeId){
24187             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24188         }
24189         
24190         this.gMapContext = this.GMapContext();
24191         
24192         this.initOverlayView();
24193         
24194         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24195         
24196         var _this = this;
24197                 
24198         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24199             _this.setPosition(_this.gMapContext.marker.position);
24200         });
24201         
24202         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24203             _this.fireEvent('mapClick', this, event);
24204             
24205         });
24206
24207         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24208             _this.fireEvent('mapRightClick', this, event);
24209             
24210         });
24211         
24212         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24213             _this.fireEvent('markerClick', this, event);
24214             
24215         });
24216
24217         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24218             _this.fireEvent('markerRightClick', this, event);
24219             
24220         });
24221         
24222         this.setPosition(this.gMapContext.location);
24223         
24224         this.fireEvent('initial', this, this.gMapContext.location);
24225     },
24226     
24227     initOverlayView: function()
24228     {
24229         var _this = this;
24230         
24231         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24232             
24233             draw: function()
24234             {
24235                 _this.fireEvent('OverlayViewDraw', _this);
24236             },
24237             
24238             onAdd: function()
24239             {
24240                 _this.fireEvent('OverlayViewOnAdd', _this);
24241             },
24242             
24243             onRemove: function()
24244             {
24245                 _this.fireEvent('OverlayViewOnRemove', _this);
24246             },
24247             
24248             show: function(cpx)
24249             {
24250                 _this.fireEvent('OverlayViewShow', _this, cpx);
24251             },
24252             
24253             hide: function()
24254             {
24255                 _this.fireEvent('OverlayViewHide', _this);
24256             }
24257             
24258         });
24259     },
24260     
24261     fromLatLngToContainerPixel: function(event)
24262     {
24263         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24264     },
24265     
24266     isApplied: function() 
24267     {
24268         return this.getGmapContext() == false ? false : true;
24269     },
24270     
24271     getGmapContext: function() 
24272     {
24273         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24274     },
24275     
24276     GMapContext: function() 
24277     {
24278         var position = new google.maps.LatLng(this.latitude, this.longitude);
24279         
24280         var _map = new google.maps.Map(this.el.dom, {
24281             center: position,
24282             zoom: this.zoom,
24283             mapTypeId: this.mapTypeId,
24284             mapTypeControl: this.mapTypeControl,
24285             disableDoubleClickZoom: this.disableDoubleClickZoom,
24286             scrollwheel: this.scrollwheel,
24287             streetViewControl: this.streetViewControl,
24288             locationName: this.locationName,
24289             draggable: this.draggable,
24290             enableAutocomplete: this.enableAutocomplete,
24291             enableReverseGeocode: this.enableReverseGeocode
24292         });
24293         
24294         var _marker = new google.maps.Marker({
24295             position: position,
24296             map: _map,
24297             title: this.markerTitle,
24298             draggable: this.draggable
24299         });
24300         
24301         return {
24302             map: _map,
24303             marker: _marker,
24304             circle: null,
24305             location: position,
24306             radius: this.radius,
24307             locationName: this.locationName,
24308             addressComponents: {
24309                 formatted_address: null,
24310                 addressLine1: null,
24311                 addressLine2: null,
24312                 streetName: null,
24313                 streetNumber: null,
24314                 city: null,
24315                 district: null,
24316                 state: null,
24317                 stateOrProvince: null
24318             },
24319             settings: this,
24320             domContainer: this.el.dom,
24321             geodecoder: new google.maps.Geocoder()
24322         };
24323     },
24324     
24325     drawCircle: function(center, radius, options) 
24326     {
24327         if (this.gMapContext.circle != null) {
24328             this.gMapContext.circle.setMap(null);
24329         }
24330         if (radius > 0) {
24331             radius *= 1;
24332             options = Roo.apply({}, options, {
24333                 strokeColor: "#0000FF",
24334                 strokeOpacity: .35,
24335                 strokeWeight: 2,
24336                 fillColor: "#0000FF",
24337                 fillOpacity: .2
24338             });
24339             
24340             options.map = this.gMapContext.map;
24341             options.radius = radius;
24342             options.center = center;
24343             this.gMapContext.circle = new google.maps.Circle(options);
24344             return this.gMapContext.circle;
24345         }
24346         
24347         return null;
24348     },
24349     
24350     setPosition: function(location) 
24351     {
24352         this.gMapContext.location = location;
24353         this.gMapContext.marker.setPosition(location);
24354         this.gMapContext.map.panTo(location);
24355         this.drawCircle(location, this.gMapContext.radius, {});
24356         
24357         var _this = this;
24358         
24359         if (this.gMapContext.settings.enableReverseGeocode) {
24360             this.gMapContext.geodecoder.geocode({
24361                 latLng: this.gMapContext.location
24362             }, function(results, status) {
24363                 
24364                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24365                     _this.gMapContext.locationName = results[0].formatted_address;
24366                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24367                     
24368                     _this.fireEvent('positionchanged', this, location);
24369                 }
24370             });
24371             
24372             return;
24373         }
24374         
24375         this.fireEvent('positionchanged', this, location);
24376     },
24377     
24378     resize: function()
24379     {
24380         google.maps.event.trigger(this.gMapContext.map, "resize");
24381         
24382         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24383         
24384         this.fireEvent('resize', this);
24385     },
24386     
24387     setPositionByLatLng: function(latitude, longitude)
24388     {
24389         this.setPosition(new google.maps.LatLng(latitude, longitude));
24390     },
24391     
24392     getCurrentPosition: function() 
24393     {
24394         return {
24395             latitude: this.gMapContext.location.lat(),
24396             longitude: this.gMapContext.location.lng()
24397         };
24398     },
24399     
24400     getAddressName: function() 
24401     {
24402         return this.gMapContext.locationName;
24403     },
24404     
24405     getAddressComponents: function() 
24406     {
24407         return this.gMapContext.addressComponents;
24408     },
24409     
24410     address_component_from_google_geocode: function(address_components) 
24411     {
24412         var result = {};
24413         
24414         for (var i = 0; i < address_components.length; i++) {
24415             var component = address_components[i];
24416             if (component.types.indexOf("postal_code") >= 0) {
24417                 result.postalCode = component.short_name;
24418             } else if (component.types.indexOf("street_number") >= 0) {
24419                 result.streetNumber = component.short_name;
24420             } else if (component.types.indexOf("route") >= 0) {
24421                 result.streetName = component.short_name;
24422             } else if (component.types.indexOf("neighborhood") >= 0) {
24423                 result.city = component.short_name;
24424             } else if (component.types.indexOf("locality") >= 0) {
24425                 result.city = component.short_name;
24426             } else if (component.types.indexOf("sublocality") >= 0) {
24427                 result.district = component.short_name;
24428             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24429                 result.stateOrProvince = component.short_name;
24430             } else if (component.types.indexOf("country") >= 0) {
24431                 result.country = component.short_name;
24432             }
24433         }
24434         
24435         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24436         result.addressLine2 = "";
24437         return result;
24438     },
24439     
24440     setZoomLevel: function(zoom)
24441     {
24442         this.gMapContext.map.setZoom(zoom);
24443     },
24444     
24445     show: function()
24446     {
24447         if(!this.el){
24448             return;
24449         }
24450         
24451         this.el.show();
24452         
24453         this.resize();
24454         
24455         this.fireEvent('show', this);
24456     },
24457     
24458     hide: function()
24459     {
24460         if(!this.el){
24461             return;
24462         }
24463         
24464         this.el.hide();
24465         
24466         this.fireEvent('hide', this);
24467     }
24468     
24469 });
24470
24471 Roo.apply(Roo.bootstrap.LocationPicker, {
24472     
24473     OverlayView : function(map, options)
24474     {
24475         options = options || {};
24476         
24477         this.setMap(map);
24478     }
24479     
24480     
24481 });/*
24482  * - LGPL
24483  *
24484  * Alert
24485  * 
24486  */
24487
24488 /**
24489  * @class Roo.bootstrap.Alert
24490  * @extends Roo.bootstrap.Component
24491  * Bootstrap Alert class
24492  * @cfg {String} title The title of alert
24493  * @cfg {String} html The content of alert
24494  * @cfg {String} weight (  success | info | warning | danger )
24495  * @cfg {String} faicon font-awesomeicon
24496  * 
24497  * @constructor
24498  * Create a new alert
24499  * @param {Object} config The config object
24500  */
24501
24502
24503 Roo.bootstrap.Alert = function(config){
24504     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24505     
24506 };
24507
24508 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24509     
24510     title: '',
24511     html: '',
24512     weight: false,
24513     faicon: false,
24514     
24515     getAutoCreate : function()
24516     {
24517         
24518         var cfg = {
24519             tag : 'div',
24520             cls : 'alert',
24521             cn : [
24522                 {
24523                     tag : 'i',
24524                     cls : 'roo-alert-icon'
24525                     
24526                 },
24527                 {
24528                     tag : 'b',
24529                     cls : 'roo-alert-title',
24530                     html : this.title
24531                 },
24532                 {
24533                     tag : 'span',
24534                     cls : 'roo-alert-text',
24535                     html : this.html
24536                 }
24537             ]
24538         };
24539         
24540         if(this.faicon){
24541             cfg.cn[0].cls += ' fa ' + this.faicon;
24542         }
24543         
24544         if(this.weight){
24545             cfg.cls += ' alert-' + this.weight;
24546         }
24547         
24548         return cfg;
24549     },
24550     
24551     initEvents: function() 
24552     {
24553         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24554     },
24555     
24556     setTitle : function(str)
24557     {
24558         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24559     },
24560     
24561     setText : function(str)
24562     {
24563         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24564     },
24565     
24566     setWeight : function(weight)
24567     {
24568         if(this.weight){
24569             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24570         }
24571         
24572         this.weight = weight;
24573         
24574         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24575     },
24576     
24577     setIcon : function(icon)
24578     {
24579         if(this.faicon){
24580             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24581         }
24582         
24583         this.faicon = icon;
24584         
24585         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24586     },
24587     
24588     hide: function() 
24589     {
24590         this.el.hide();   
24591     },
24592     
24593     show: function() 
24594     {  
24595         this.el.show();   
24596     }
24597     
24598 });
24599
24600  
24601 /*
24602 * Licence: LGPL
24603 */
24604
24605 /**
24606  * @class Roo.bootstrap.UploadCropbox
24607  * @extends Roo.bootstrap.Component
24608  * Bootstrap UploadCropbox class
24609  * @cfg {String} emptyText show when image has been loaded
24610  * @cfg {String} rotateNotify show when image too small to rotate
24611  * @cfg {Number} errorTimeout default 3000
24612  * @cfg {Number} minWidth default 300
24613  * @cfg {Number} minHeight default 300
24614  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24615  * @cfg {Boolean} isDocument (true|false) default false
24616  * @cfg {String} url action url
24617  * @cfg {String} paramName default 'imageUpload'
24618  * @cfg {String} method default POST
24619  * @cfg {Boolean} loadMask (true|false) default true
24620  * @cfg {Boolean} loadingText default 'Loading...'
24621  * 
24622  * @constructor
24623  * Create a new UploadCropbox
24624  * @param {Object} config The config object
24625  */
24626
24627 Roo.bootstrap.UploadCropbox = function(config){
24628     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24629     
24630     this.addEvents({
24631         /**
24632          * @event beforeselectfile
24633          * Fire before select file
24634          * @param {Roo.bootstrap.UploadCropbox} this
24635          */
24636         "beforeselectfile" : true,
24637         /**
24638          * @event initial
24639          * Fire after initEvent
24640          * @param {Roo.bootstrap.UploadCropbox} this
24641          */
24642         "initial" : true,
24643         /**
24644          * @event crop
24645          * Fire after initEvent
24646          * @param {Roo.bootstrap.UploadCropbox} this
24647          * @param {String} data
24648          */
24649         "crop" : true,
24650         /**
24651          * @event prepare
24652          * Fire when preparing the file data
24653          * @param {Roo.bootstrap.UploadCropbox} this
24654          * @param {Object} file
24655          */
24656         "prepare" : true,
24657         /**
24658          * @event exception
24659          * Fire when get exception
24660          * @param {Roo.bootstrap.UploadCropbox} this
24661          * @param {XMLHttpRequest} xhr
24662          */
24663         "exception" : true,
24664         /**
24665          * @event beforeloadcanvas
24666          * Fire before load the canvas
24667          * @param {Roo.bootstrap.UploadCropbox} this
24668          * @param {String} src
24669          */
24670         "beforeloadcanvas" : true,
24671         /**
24672          * @event trash
24673          * Fire when trash image
24674          * @param {Roo.bootstrap.UploadCropbox} this
24675          */
24676         "trash" : true,
24677         /**
24678          * @event download
24679          * Fire when download the image
24680          * @param {Roo.bootstrap.UploadCropbox} this
24681          */
24682         "download" : true,
24683         /**
24684          * @event footerbuttonclick
24685          * Fire when footerbuttonclick
24686          * @param {Roo.bootstrap.UploadCropbox} this
24687          * @param {String} type
24688          */
24689         "footerbuttonclick" : true,
24690         /**
24691          * @event resize
24692          * Fire when resize
24693          * @param {Roo.bootstrap.UploadCropbox} this
24694          */
24695         "resize" : true,
24696         /**
24697          * @event rotate
24698          * Fire when rotate the image
24699          * @param {Roo.bootstrap.UploadCropbox} this
24700          * @param {String} pos
24701          */
24702         "rotate" : true,
24703         /**
24704          * @event inspect
24705          * Fire when inspect the file
24706          * @param {Roo.bootstrap.UploadCropbox} this
24707          * @param {Object} file
24708          */
24709         "inspect" : true,
24710         /**
24711          * @event upload
24712          * Fire when xhr upload the file
24713          * @param {Roo.bootstrap.UploadCropbox} this
24714          * @param {Object} data
24715          */
24716         "upload" : true,
24717         /**
24718          * @event arrange
24719          * Fire when arrange the file data
24720          * @param {Roo.bootstrap.UploadCropbox} this
24721          * @param {Object} formData
24722          */
24723         "arrange" : true
24724     });
24725     
24726     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
24727 };
24728
24729 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
24730     
24731     emptyText : 'Click to upload image',
24732     rotateNotify : 'Image is too small to rotate',
24733     errorTimeout : 3000,
24734     scale : 0,
24735     baseScale : 1,
24736     rotate : 0,
24737     dragable : false,
24738     pinching : false,
24739     mouseX : 0,
24740     mouseY : 0,
24741     cropData : false,
24742     minWidth : 300,
24743     minHeight : 300,
24744     file : false,
24745     exif : {},
24746     baseRotate : 1,
24747     cropType : 'image/jpeg',
24748     buttons : false,
24749     canvasLoaded : false,
24750     isDocument : false,
24751     method : 'POST',
24752     paramName : 'imageUpload',
24753     loadMask : true,
24754     loadingText : 'Loading...',
24755     maskEl : false,
24756     
24757     getAutoCreate : function()
24758     {
24759         var cfg = {
24760             tag : 'div',
24761             cls : 'roo-upload-cropbox',
24762             cn : [
24763                 {
24764                     tag : 'input',
24765                     cls : 'roo-upload-cropbox-selector',
24766                     type : 'file'
24767                 },
24768                 {
24769                     tag : 'div',
24770                     cls : 'roo-upload-cropbox-body',
24771                     style : 'cursor:pointer',
24772                     cn : [
24773                         {
24774                             tag : 'div',
24775                             cls : 'roo-upload-cropbox-preview'
24776                         },
24777                         {
24778                             tag : 'div',
24779                             cls : 'roo-upload-cropbox-thumb'
24780                         },
24781                         {
24782                             tag : 'div',
24783                             cls : 'roo-upload-cropbox-empty-notify',
24784                             html : this.emptyText
24785                         },
24786                         {
24787                             tag : 'div',
24788                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
24789                             html : this.rotateNotify
24790                         }
24791                     ]
24792                 },
24793                 {
24794                     tag : 'div',
24795                     cls : 'roo-upload-cropbox-footer',
24796                     cn : {
24797                         tag : 'div',
24798                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
24799                         cn : []
24800                     }
24801                 }
24802             ]
24803         };
24804         
24805         return cfg;
24806     },
24807     
24808     onRender : function(ct, position)
24809     {
24810         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
24811         
24812         if (this.buttons.length) {
24813             
24814             Roo.each(this.buttons, function(bb) {
24815                 
24816                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
24817                 
24818                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
24819                 
24820             }, this);
24821         }
24822         
24823         if(this.loadMask){
24824             this.maskEl = this.el;
24825         }
24826     },
24827     
24828     initEvents : function()
24829     {
24830         this.urlAPI = (window.createObjectURL && window) || 
24831                                 (window.URL && URL.revokeObjectURL && URL) || 
24832                                 (window.webkitURL && webkitURL);
24833                         
24834         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
24835         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24836         
24837         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
24838         this.selectorEl.hide();
24839         
24840         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
24841         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24842         
24843         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
24844         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24845         this.thumbEl.hide();
24846         
24847         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
24848         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24849         
24850         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
24851         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24852         this.errorEl.hide();
24853         
24854         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
24855         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24856         this.footerEl.hide();
24857         
24858         this.setThumbBoxSize();
24859         
24860         this.bind();
24861         
24862         this.resize();
24863         
24864         this.fireEvent('initial', this);
24865     },
24866
24867     bind : function()
24868     {
24869         var _this = this;
24870         
24871         window.addEventListener("resize", function() { _this.resize(); } );
24872         
24873         this.bodyEl.on('click', this.beforeSelectFile, this);
24874         
24875         if(Roo.isTouch){
24876             this.bodyEl.on('touchstart', this.onTouchStart, this);
24877             this.bodyEl.on('touchmove', this.onTouchMove, this);
24878             this.bodyEl.on('touchend', this.onTouchEnd, this);
24879         }
24880         
24881         if(!Roo.isTouch){
24882             this.bodyEl.on('mousedown', this.onMouseDown, this);
24883             this.bodyEl.on('mousemove', this.onMouseMove, this);
24884             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
24885             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
24886             Roo.get(document).on('mouseup', this.onMouseUp, this);
24887         }
24888         
24889         this.selectorEl.on('change', this.onFileSelected, this);
24890     },
24891     
24892     reset : function()
24893     {    
24894         this.scale = 0;
24895         this.baseScale = 1;
24896         this.rotate = 0;
24897         this.baseRotate = 1;
24898         this.dragable = false;
24899         this.pinching = false;
24900         this.mouseX = 0;
24901         this.mouseY = 0;
24902         this.cropData = false;
24903         this.notifyEl.dom.innerHTML = this.emptyText;
24904         
24905         this.selectorEl.dom.value = '';
24906         
24907     },
24908     
24909     resize : function()
24910     {
24911         if(this.fireEvent('resize', this) != false){
24912             this.setThumbBoxPosition();
24913             this.setCanvasPosition();
24914         }
24915     },
24916     
24917     onFooterButtonClick : function(e, el, o, type)
24918     {
24919         switch (type) {
24920             case 'rotate-left' :
24921                 this.onRotateLeft(e);
24922                 break;
24923             case 'rotate-right' :
24924                 this.onRotateRight(e);
24925                 break;
24926             case 'picture' :
24927                 this.beforeSelectFile(e);
24928                 break;
24929             case 'trash' :
24930                 this.trash(e);
24931                 break;
24932             case 'crop' :
24933                 this.crop(e);
24934                 break;
24935             case 'download' :
24936                 this.download(e);
24937                 break;
24938             default :
24939                 break;
24940         }
24941         
24942         this.fireEvent('footerbuttonclick', this, type);
24943     },
24944     
24945     beforeSelectFile : function(e)
24946     {
24947         e.preventDefault();
24948         
24949         if(this.fireEvent('beforeselectfile', this) != false){
24950             this.selectorEl.dom.click();
24951         }
24952     },
24953     
24954     onFileSelected : function(e)
24955     {
24956         e.preventDefault();
24957         
24958         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
24959             return;
24960         }
24961         
24962         var file = this.selectorEl.dom.files[0];
24963         
24964         if(this.fireEvent('inspect', this, file) != false){
24965             this.prepare(file);
24966         }
24967         
24968     },
24969     
24970     trash : function(e)
24971     {
24972         this.fireEvent('trash', this);
24973     },
24974     
24975     download : function(e)
24976     {
24977         this.fireEvent('download', this);
24978     },
24979     
24980     loadCanvas : function(src)
24981     {   
24982         if(this.fireEvent('beforeloadcanvas', this, src) != false){
24983             
24984             this.reset();
24985             
24986             this.imageEl = document.createElement('img');
24987             
24988             var _this = this;
24989             
24990             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
24991             
24992             this.imageEl.src = src;
24993         }
24994     },
24995     
24996     onLoadCanvas : function()
24997     {   
24998         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
24999         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25000         
25001         this.bodyEl.un('click', this.beforeSelectFile, this);
25002         
25003         this.notifyEl.hide();
25004         this.thumbEl.show();
25005         this.footerEl.show();
25006         
25007         this.baseRotateLevel();
25008         
25009         if(this.isDocument){
25010             this.setThumbBoxSize();
25011         }
25012         
25013         this.setThumbBoxPosition();
25014         
25015         this.baseScaleLevel();
25016         
25017         this.draw();
25018         
25019         this.resize();
25020         
25021         this.canvasLoaded = true;
25022         
25023         if(this.loadMask){
25024             this.maskEl.unmask();
25025         }
25026         
25027     },
25028     
25029     setCanvasPosition : function()
25030     {   
25031         if(!this.canvasEl){
25032             return;
25033         }
25034         
25035         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25036         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25037         
25038         this.previewEl.setLeft(pw);
25039         this.previewEl.setTop(ph);
25040         
25041     },
25042     
25043     onMouseDown : function(e)
25044     {   
25045         e.stopEvent();
25046         
25047         this.dragable = true;
25048         this.pinching = false;
25049         
25050         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25051             this.dragable = false;
25052             return;
25053         }
25054         
25055         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25056         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25057         
25058     },
25059     
25060     onMouseMove : function(e)
25061     {   
25062         e.stopEvent();
25063         
25064         if(!this.canvasLoaded){
25065             return;
25066         }
25067         
25068         if (!this.dragable){
25069             return;
25070         }
25071         
25072         var minX = Math.ceil(this.thumbEl.getLeft(true));
25073         var minY = Math.ceil(this.thumbEl.getTop(true));
25074         
25075         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25076         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25077         
25078         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25079         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25080         
25081         x = x - this.mouseX;
25082         y = y - this.mouseY;
25083         
25084         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25085         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25086         
25087         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25088         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25089         
25090         this.previewEl.setLeft(bgX);
25091         this.previewEl.setTop(bgY);
25092         
25093         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25094         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25095     },
25096     
25097     onMouseUp : function(e)
25098     {   
25099         e.stopEvent();
25100         
25101         this.dragable = false;
25102     },
25103     
25104     onMouseWheel : function(e)
25105     {   
25106         e.stopEvent();
25107         
25108         this.startScale = this.scale;
25109         
25110         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25111         
25112         if(!this.zoomable()){
25113             this.scale = this.startScale;
25114             return;
25115         }
25116         
25117         this.draw();
25118         
25119         return;
25120     },
25121     
25122     zoomable : function()
25123     {
25124         var minScale = this.thumbEl.getWidth() / this.minWidth;
25125         
25126         if(this.minWidth < this.minHeight){
25127             minScale = this.thumbEl.getHeight() / this.minHeight;
25128         }
25129         
25130         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25131         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25132         
25133         if(
25134                 this.isDocument &&
25135                 (this.rotate == 0 || this.rotate == 180) && 
25136                 (
25137                     width > this.imageEl.OriginWidth || 
25138                     height > this.imageEl.OriginHeight ||
25139                     (width < this.minWidth && height < this.minHeight)
25140                 )
25141         ){
25142             return false;
25143         }
25144         
25145         if(
25146                 this.isDocument &&
25147                 (this.rotate == 90 || this.rotate == 270) && 
25148                 (
25149                     width > this.imageEl.OriginWidth || 
25150                     height > this.imageEl.OriginHeight ||
25151                     (width < this.minHeight && height < this.minWidth)
25152                 )
25153         ){
25154             return false;
25155         }
25156         
25157         if(
25158                 !this.isDocument &&
25159                 (this.rotate == 0 || this.rotate == 180) && 
25160                 (
25161                     width < this.minWidth || 
25162                     width > this.imageEl.OriginWidth || 
25163                     height < this.minHeight || 
25164                     height > this.imageEl.OriginHeight
25165                 )
25166         ){
25167             return false;
25168         }
25169         
25170         if(
25171                 !this.isDocument &&
25172                 (this.rotate == 90 || this.rotate == 270) && 
25173                 (
25174                     width < this.minHeight || 
25175                     width > this.imageEl.OriginWidth || 
25176                     height < this.minWidth || 
25177                     height > this.imageEl.OriginHeight
25178                 )
25179         ){
25180             return false;
25181         }
25182         
25183         return true;
25184         
25185     },
25186     
25187     onRotateLeft : function(e)
25188     {   
25189         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25190             
25191             var minScale = this.thumbEl.getWidth() / this.minWidth;
25192             
25193             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25194             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25195             
25196             this.startScale = this.scale;
25197             
25198             while (this.getScaleLevel() < minScale){
25199             
25200                 this.scale = this.scale + 1;
25201                 
25202                 if(!this.zoomable()){
25203                     break;
25204                 }
25205                 
25206                 if(
25207                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25208                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25209                 ){
25210                     continue;
25211                 }
25212                 
25213                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25214
25215                 this.draw();
25216                 
25217                 return;
25218             }
25219             
25220             this.scale = this.startScale;
25221             
25222             this.onRotateFail();
25223             
25224             return false;
25225         }
25226         
25227         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25228
25229         if(this.isDocument){
25230             this.setThumbBoxSize();
25231             this.setThumbBoxPosition();
25232             this.setCanvasPosition();
25233         }
25234         
25235         this.draw();
25236         
25237         this.fireEvent('rotate', this, 'left');
25238         
25239     },
25240     
25241     onRotateRight : function(e)
25242     {
25243         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25244             
25245             var minScale = this.thumbEl.getWidth() / this.minWidth;
25246         
25247             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25248             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25249             
25250             this.startScale = this.scale;
25251             
25252             while (this.getScaleLevel() < minScale){
25253             
25254                 this.scale = this.scale + 1;
25255                 
25256                 if(!this.zoomable()){
25257                     break;
25258                 }
25259                 
25260                 if(
25261                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25262                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25263                 ){
25264                     continue;
25265                 }
25266                 
25267                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25268
25269                 this.draw();
25270                 
25271                 return;
25272             }
25273             
25274             this.scale = this.startScale;
25275             
25276             this.onRotateFail();
25277             
25278             return false;
25279         }
25280         
25281         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25282
25283         if(this.isDocument){
25284             this.setThumbBoxSize();
25285             this.setThumbBoxPosition();
25286             this.setCanvasPosition();
25287         }
25288         
25289         this.draw();
25290         
25291         this.fireEvent('rotate', this, 'right');
25292     },
25293     
25294     onRotateFail : function()
25295     {
25296         this.errorEl.show(true);
25297         
25298         var _this = this;
25299         
25300         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25301     },
25302     
25303     draw : function()
25304     {
25305         this.previewEl.dom.innerHTML = '';
25306         
25307         var canvasEl = document.createElement("canvas");
25308         
25309         var contextEl = canvasEl.getContext("2d");
25310         
25311         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25312         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25313         var center = this.imageEl.OriginWidth / 2;
25314         
25315         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25316             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25317             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25318             center = this.imageEl.OriginHeight / 2;
25319         }
25320         
25321         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25322         
25323         contextEl.translate(center, center);
25324         contextEl.rotate(this.rotate * Math.PI / 180);
25325
25326         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25327         
25328         this.canvasEl = document.createElement("canvas");
25329         
25330         this.contextEl = this.canvasEl.getContext("2d");
25331         
25332         switch (this.rotate) {
25333             case 0 :
25334                 
25335                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25336                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25337                 
25338                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25339                 
25340                 break;
25341             case 90 : 
25342                 
25343                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25344                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25345                 
25346                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25347                     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);
25348                     break;
25349                 }
25350                 
25351                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25352                 
25353                 break;
25354             case 180 :
25355                 
25356                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25357                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25358                 
25359                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25360                     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);
25361                     break;
25362                 }
25363                 
25364                 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);
25365                 
25366                 break;
25367             case 270 :
25368                 
25369                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25370                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25371         
25372                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25373                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25374                     break;
25375                 }
25376                 
25377                 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);
25378                 
25379                 break;
25380             default : 
25381                 break;
25382         }
25383         
25384         this.previewEl.appendChild(this.canvasEl);
25385         
25386         this.setCanvasPosition();
25387     },
25388     
25389     crop : function()
25390     {
25391         if(!this.canvasLoaded){
25392             return;
25393         }
25394         
25395         var imageCanvas = document.createElement("canvas");
25396         
25397         var imageContext = imageCanvas.getContext("2d");
25398         
25399         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25400         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25401         
25402         var center = imageCanvas.width / 2;
25403         
25404         imageContext.translate(center, center);
25405         
25406         imageContext.rotate(this.rotate * Math.PI / 180);
25407         
25408         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25409         
25410         var canvas = document.createElement("canvas");
25411         
25412         var context = canvas.getContext("2d");
25413                 
25414         canvas.width = this.minWidth;
25415         canvas.height = this.minHeight;
25416
25417         switch (this.rotate) {
25418             case 0 :
25419                 
25420                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25421                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25422                 
25423                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25424                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25425                 
25426                 var targetWidth = this.minWidth - 2 * x;
25427                 var targetHeight = this.minHeight - 2 * y;
25428                 
25429                 var scale = 1;
25430                 
25431                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25432                     scale = targetWidth / width;
25433                 }
25434                 
25435                 if(x > 0 && y == 0){
25436                     scale = targetHeight / height;
25437                 }
25438                 
25439                 if(x > 0 && y > 0){
25440                     scale = targetWidth / width;
25441                     
25442                     if(width < height){
25443                         scale = targetHeight / height;
25444                     }
25445                 }
25446                 
25447                 context.scale(scale, scale);
25448                 
25449                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25450                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25451
25452                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25453                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25454
25455                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25456                 
25457                 break;
25458             case 90 : 
25459                 
25460                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25461                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25462                 
25463                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25464                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25465                 
25466                 var targetWidth = this.minWidth - 2 * x;
25467                 var targetHeight = this.minHeight - 2 * y;
25468                 
25469                 var scale = 1;
25470                 
25471                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25472                     scale = targetWidth / width;
25473                 }
25474                 
25475                 if(x > 0 && y == 0){
25476                     scale = targetHeight / height;
25477                 }
25478                 
25479                 if(x > 0 && y > 0){
25480                     scale = targetWidth / width;
25481                     
25482                     if(width < height){
25483                         scale = targetHeight / height;
25484                     }
25485                 }
25486                 
25487                 context.scale(scale, scale);
25488                 
25489                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25490                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25491
25492                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25493                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25494                 
25495                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25496                 
25497                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25498                 
25499                 break;
25500             case 180 :
25501                 
25502                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25503                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25504                 
25505                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25506                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25507                 
25508                 var targetWidth = this.minWidth - 2 * x;
25509                 var targetHeight = this.minHeight - 2 * y;
25510                 
25511                 var scale = 1;
25512                 
25513                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25514                     scale = targetWidth / width;
25515                 }
25516                 
25517                 if(x > 0 && y == 0){
25518                     scale = targetHeight / height;
25519                 }
25520                 
25521                 if(x > 0 && y > 0){
25522                     scale = targetWidth / width;
25523                     
25524                     if(width < height){
25525                         scale = targetHeight / height;
25526                     }
25527                 }
25528                 
25529                 context.scale(scale, scale);
25530                 
25531                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25532                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25533
25534                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25535                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25536
25537                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25538                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25539                 
25540                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25541                 
25542                 break;
25543             case 270 :
25544                 
25545                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25546                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25547                 
25548                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25549                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25550                 
25551                 var targetWidth = this.minWidth - 2 * x;
25552                 var targetHeight = this.minHeight - 2 * y;
25553                 
25554                 var scale = 1;
25555                 
25556                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25557                     scale = targetWidth / width;
25558                 }
25559                 
25560                 if(x > 0 && y == 0){
25561                     scale = targetHeight / height;
25562                 }
25563                 
25564                 if(x > 0 && y > 0){
25565                     scale = targetWidth / width;
25566                     
25567                     if(width < height){
25568                         scale = targetHeight / height;
25569                     }
25570                 }
25571                 
25572                 context.scale(scale, scale);
25573                 
25574                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25575                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25576
25577                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25578                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25579                 
25580                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25581                 
25582                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25583                 
25584                 break;
25585             default : 
25586                 break;
25587         }
25588         
25589         this.cropData = canvas.toDataURL(this.cropType);
25590         
25591         if(this.fireEvent('crop', this, this.cropData) !== false){
25592             this.process(this.file, this.cropData);
25593         }
25594         
25595         return;
25596         
25597     },
25598     
25599     setThumbBoxSize : function()
25600     {
25601         var width, height;
25602         
25603         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25604             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25605             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25606             
25607             this.minWidth = width;
25608             this.minHeight = height;
25609             
25610             if(this.rotate == 90 || this.rotate == 270){
25611                 this.minWidth = height;
25612                 this.minHeight = width;
25613             }
25614         }
25615         
25616         height = 300;
25617         width = Math.ceil(this.minWidth * height / this.minHeight);
25618         
25619         if(this.minWidth > this.minHeight){
25620             width = 300;
25621             height = Math.ceil(this.minHeight * width / this.minWidth);
25622         }
25623         
25624         this.thumbEl.setStyle({
25625             width : width + 'px',
25626             height : height + 'px'
25627         });
25628
25629         return;
25630             
25631     },
25632     
25633     setThumbBoxPosition : function()
25634     {
25635         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25636         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25637         
25638         this.thumbEl.setLeft(x);
25639         this.thumbEl.setTop(y);
25640         
25641     },
25642     
25643     baseRotateLevel : function()
25644     {
25645         this.baseRotate = 1;
25646         
25647         if(
25648                 typeof(this.exif) != 'undefined' &&
25649                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25650                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25651         ){
25652             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25653         }
25654         
25655         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25656         
25657     },
25658     
25659     baseScaleLevel : function()
25660     {
25661         var width, height;
25662         
25663         if(this.isDocument){
25664             
25665             if(this.baseRotate == 6 || this.baseRotate == 8){
25666             
25667                 height = this.thumbEl.getHeight();
25668                 this.baseScale = height / this.imageEl.OriginWidth;
25669
25670                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
25671                     width = this.thumbEl.getWidth();
25672                     this.baseScale = width / this.imageEl.OriginHeight;
25673                 }
25674
25675                 return;
25676             }
25677
25678             height = this.thumbEl.getHeight();
25679             this.baseScale = height / this.imageEl.OriginHeight;
25680
25681             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
25682                 width = this.thumbEl.getWidth();
25683                 this.baseScale = width / this.imageEl.OriginWidth;
25684             }
25685
25686             return;
25687         }
25688         
25689         if(this.baseRotate == 6 || this.baseRotate == 8){
25690             
25691             width = this.thumbEl.getHeight();
25692             this.baseScale = width / this.imageEl.OriginHeight;
25693             
25694             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
25695                 height = this.thumbEl.getWidth();
25696                 this.baseScale = height / this.imageEl.OriginHeight;
25697             }
25698             
25699             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25700                 height = this.thumbEl.getWidth();
25701                 this.baseScale = height / this.imageEl.OriginHeight;
25702                 
25703                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
25704                     width = this.thumbEl.getHeight();
25705                     this.baseScale = width / this.imageEl.OriginWidth;
25706                 }
25707             }
25708             
25709             return;
25710         }
25711         
25712         width = this.thumbEl.getWidth();
25713         this.baseScale = width / this.imageEl.OriginWidth;
25714         
25715         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
25716             height = this.thumbEl.getHeight();
25717             this.baseScale = height / this.imageEl.OriginHeight;
25718         }
25719         
25720         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25721             
25722             height = this.thumbEl.getHeight();
25723             this.baseScale = height / this.imageEl.OriginHeight;
25724             
25725             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
25726                 width = this.thumbEl.getWidth();
25727                 this.baseScale = width / this.imageEl.OriginWidth;
25728             }
25729             
25730         }
25731         
25732         return;
25733     },
25734     
25735     getScaleLevel : function()
25736     {
25737         return this.baseScale * Math.pow(1.1, this.scale);
25738     },
25739     
25740     onTouchStart : function(e)
25741     {
25742         if(!this.canvasLoaded){
25743             this.beforeSelectFile(e);
25744             return;
25745         }
25746         
25747         var touches = e.browserEvent.touches;
25748         
25749         if(!touches){
25750             return;
25751         }
25752         
25753         if(touches.length == 1){
25754             this.onMouseDown(e);
25755             return;
25756         }
25757         
25758         if(touches.length != 2){
25759             return;
25760         }
25761         
25762         var coords = [];
25763         
25764         for(var i = 0, finger; finger = touches[i]; i++){
25765             coords.push(finger.pageX, finger.pageY);
25766         }
25767         
25768         var x = Math.pow(coords[0] - coords[2], 2);
25769         var y = Math.pow(coords[1] - coords[3], 2);
25770         
25771         this.startDistance = Math.sqrt(x + y);
25772         
25773         this.startScale = this.scale;
25774         
25775         this.pinching = true;
25776         this.dragable = false;
25777         
25778     },
25779     
25780     onTouchMove : function(e)
25781     {
25782         if(!this.pinching && !this.dragable){
25783             return;
25784         }
25785         
25786         var touches = e.browserEvent.touches;
25787         
25788         if(!touches){
25789             return;
25790         }
25791         
25792         if(this.dragable){
25793             this.onMouseMove(e);
25794             return;
25795         }
25796         
25797         var coords = [];
25798         
25799         for(var i = 0, finger; finger = touches[i]; i++){
25800             coords.push(finger.pageX, finger.pageY);
25801         }
25802         
25803         var x = Math.pow(coords[0] - coords[2], 2);
25804         var y = Math.pow(coords[1] - coords[3], 2);
25805         
25806         this.endDistance = Math.sqrt(x + y);
25807         
25808         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
25809         
25810         if(!this.zoomable()){
25811             this.scale = this.startScale;
25812             return;
25813         }
25814         
25815         this.draw();
25816         
25817     },
25818     
25819     onTouchEnd : function(e)
25820     {
25821         this.pinching = false;
25822         this.dragable = false;
25823         
25824     },
25825     
25826     process : function(file, crop)
25827     {
25828         if(this.loadMask){
25829             this.maskEl.mask(this.loadingText);
25830         }
25831         
25832         this.xhr = new XMLHttpRequest();
25833         
25834         file.xhr = this.xhr;
25835
25836         this.xhr.open(this.method, this.url, true);
25837         
25838         var headers = {
25839             "Accept": "application/json",
25840             "Cache-Control": "no-cache",
25841             "X-Requested-With": "XMLHttpRequest"
25842         };
25843         
25844         for (var headerName in headers) {
25845             var headerValue = headers[headerName];
25846             if (headerValue) {
25847                 this.xhr.setRequestHeader(headerName, headerValue);
25848             }
25849         }
25850         
25851         var _this = this;
25852         
25853         this.xhr.onload = function()
25854         {
25855             _this.xhrOnLoad(_this.xhr);
25856         }
25857         
25858         this.xhr.onerror = function()
25859         {
25860             _this.xhrOnError(_this.xhr);
25861         }
25862         
25863         var formData = new FormData();
25864
25865         formData.append('returnHTML', 'NO');
25866         
25867         if(crop){
25868             formData.append('crop', crop);
25869         }
25870         
25871         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
25872             formData.append(this.paramName, file, file.name);
25873         }
25874         
25875         if(typeof(file.filename) != 'undefined'){
25876             formData.append('filename', file.filename);
25877         }
25878         
25879         if(typeof(file.mimetype) != 'undefined'){
25880             formData.append('mimetype', file.mimetype);
25881         }
25882         
25883         if(this.fireEvent('arrange', this, formData) != false){
25884             this.xhr.send(formData);
25885         };
25886     },
25887     
25888     xhrOnLoad : function(xhr)
25889     {
25890         if(this.loadMask){
25891             this.maskEl.unmask();
25892         }
25893         
25894         if (xhr.readyState !== 4) {
25895             this.fireEvent('exception', this, xhr);
25896             return;
25897         }
25898
25899         var response = Roo.decode(xhr.responseText);
25900         
25901         if(!response.success){
25902             this.fireEvent('exception', this, xhr);
25903             return;
25904         }
25905         
25906         var response = Roo.decode(xhr.responseText);
25907         
25908         this.fireEvent('upload', this, response);
25909         
25910     },
25911     
25912     xhrOnError : function()
25913     {
25914         if(this.loadMask){
25915             this.maskEl.unmask();
25916         }
25917         
25918         Roo.log('xhr on error');
25919         
25920         var response = Roo.decode(xhr.responseText);
25921           
25922         Roo.log(response);
25923         
25924     },
25925     
25926     prepare : function(file)
25927     {   
25928         if(this.loadMask){
25929             this.maskEl.mask(this.loadingText);
25930         }
25931         
25932         this.file = false;
25933         this.exif = {};
25934         
25935         if(typeof(file) === 'string'){
25936             this.loadCanvas(file);
25937             return;
25938         }
25939         
25940         if(!file || !this.urlAPI){
25941             return;
25942         }
25943         
25944         this.file = file;
25945         this.cropType = file.type;
25946         
25947         var _this = this;
25948         
25949         if(this.fireEvent('prepare', this, this.file) != false){
25950             
25951             var reader = new FileReader();
25952             
25953             reader.onload = function (e) {
25954                 if (e.target.error) {
25955                     Roo.log(e.target.error);
25956                     return;
25957                 }
25958                 
25959                 var buffer = e.target.result,
25960                     dataView = new DataView(buffer),
25961                     offset = 2,
25962                     maxOffset = dataView.byteLength - 4,
25963                     markerBytes,
25964                     markerLength;
25965                 
25966                 if (dataView.getUint16(0) === 0xffd8) {
25967                     while (offset < maxOffset) {
25968                         markerBytes = dataView.getUint16(offset);
25969                         
25970                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
25971                             markerLength = dataView.getUint16(offset + 2) + 2;
25972                             if (offset + markerLength > dataView.byteLength) {
25973                                 Roo.log('Invalid meta data: Invalid segment size.');
25974                                 break;
25975                             }
25976                             
25977                             if(markerBytes == 0xffe1){
25978                                 _this.parseExifData(
25979                                     dataView,
25980                                     offset,
25981                                     markerLength
25982                                 );
25983                             }
25984                             
25985                             offset += markerLength;
25986                             
25987                             continue;
25988                         }
25989                         
25990                         break;
25991                     }
25992                     
25993                 }
25994                 
25995                 var url = _this.urlAPI.createObjectURL(_this.file);
25996                 
25997                 _this.loadCanvas(url);
25998                 
25999                 return;
26000             }
26001             
26002             reader.readAsArrayBuffer(this.file);
26003             
26004         }
26005         
26006     },
26007     
26008     parseExifData : function(dataView, offset, length)
26009     {
26010         var tiffOffset = offset + 10,
26011             littleEndian,
26012             dirOffset;
26013     
26014         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26015             // No Exif data, might be XMP data instead
26016             return;
26017         }
26018         
26019         // Check for the ASCII code for "Exif" (0x45786966):
26020         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26021             // No Exif data, might be XMP data instead
26022             return;
26023         }
26024         if (tiffOffset + 8 > dataView.byteLength) {
26025             Roo.log('Invalid Exif data: Invalid segment size.');
26026             return;
26027         }
26028         // Check for the two null bytes:
26029         if (dataView.getUint16(offset + 8) !== 0x0000) {
26030             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26031             return;
26032         }
26033         // Check the byte alignment:
26034         switch (dataView.getUint16(tiffOffset)) {
26035         case 0x4949:
26036             littleEndian = true;
26037             break;
26038         case 0x4D4D:
26039             littleEndian = false;
26040             break;
26041         default:
26042             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26043             return;
26044         }
26045         // Check for the TIFF tag marker (0x002A):
26046         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26047             Roo.log('Invalid Exif data: Missing TIFF marker.');
26048             return;
26049         }
26050         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26051         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26052         
26053         this.parseExifTags(
26054             dataView,
26055             tiffOffset,
26056             tiffOffset + dirOffset,
26057             littleEndian
26058         );
26059     },
26060     
26061     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26062     {
26063         var tagsNumber,
26064             dirEndOffset,
26065             i;
26066         if (dirOffset + 6 > dataView.byteLength) {
26067             Roo.log('Invalid Exif data: Invalid directory offset.');
26068             return;
26069         }
26070         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26071         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26072         if (dirEndOffset + 4 > dataView.byteLength) {
26073             Roo.log('Invalid Exif data: Invalid directory size.');
26074             return;
26075         }
26076         for (i = 0; i < tagsNumber; i += 1) {
26077             this.parseExifTag(
26078                 dataView,
26079                 tiffOffset,
26080                 dirOffset + 2 + 12 * i, // tag offset
26081                 littleEndian
26082             );
26083         }
26084         // Return the offset to the next directory:
26085         return dataView.getUint32(dirEndOffset, littleEndian);
26086     },
26087     
26088     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26089     {
26090         var tag = dataView.getUint16(offset, littleEndian);
26091         
26092         this.exif[tag] = this.getExifValue(
26093             dataView,
26094             tiffOffset,
26095             offset,
26096             dataView.getUint16(offset + 2, littleEndian), // tag type
26097             dataView.getUint32(offset + 4, littleEndian), // tag length
26098             littleEndian
26099         );
26100     },
26101     
26102     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26103     {
26104         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26105             tagSize,
26106             dataOffset,
26107             values,
26108             i,
26109             str,
26110             c;
26111     
26112         if (!tagType) {
26113             Roo.log('Invalid Exif data: Invalid tag type.');
26114             return;
26115         }
26116         
26117         tagSize = tagType.size * length;
26118         // Determine if the value is contained in the dataOffset bytes,
26119         // or if the value at the dataOffset is a pointer to the actual data:
26120         dataOffset = tagSize > 4 ?
26121                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26122         if (dataOffset + tagSize > dataView.byteLength) {
26123             Roo.log('Invalid Exif data: Invalid data offset.');
26124             return;
26125         }
26126         if (length === 1) {
26127             return tagType.getValue(dataView, dataOffset, littleEndian);
26128         }
26129         values = [];
26130         for (i = 0; i < length; i += 1) {
26131             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26132         }
26133         
26134         if (tagType.ascii) {
26135             str = '';
26136             // Concatenate the chars:
26137             for (i = 0; i < values.length; i += 1) {
26138                 c = values[i];
26139                 // Ignore the terminating NULL byte(s):
26140                 if (c === '\u0000') {
26141                     break;
26142                 }
26143                 str += c;
26144             }
26145             return str;
26146         }
26147         return values;
26148     }
26149     
26150 });
26151
26152 Roo.apply(Roo.bootstrap.UploadCropbox, {
26153     tags : {
26154         'Orientation': 0x0112
26155     },
26156     
26157     Orientation: {
26158             1: 0, //'top-left',
26159 //            2: 'top-right',
26160             3: 180, //'bottom-right',
26161 //            4: 'bottom-left',
26162 //            5: 'left-top',
26163             6: 90, //'right-top',
26164 //            7: 'right-bottom',
26165             8: 270 //'left-bottom'
26166     },
26167     
26168     exifTagTypes : {
26169         // byte, 8-bit unsigned int:
26170         1: {
26171             getValue: function (dataView, dataOffset) {
26172                 return dataView.getUint8(dataOffset);
26173             },
26174             size: 1
26175         },
26176         // ascii, 8-bit byte:
26177         2: {
26178             getValue: function (dataView, dataOffset) {
26179                 return String.fromCharCode(dataView.getUint8(dataOffset));
26180             },
26181             size: 1,
26182             ascii: true
26183         },
26184         // short, 16 bit int:
26185         3: {
26186             getValue: function (dataView, dataOffset, littleEndian) {
26187                 return dataView.getUint16(dataOffset, littleEndian);
26188             },
26189             size: 2
26190         },
26191         // long, 32 bit int:
26192         4: {
26193             getValue: function (dataView, dataOffset, littleEndian) {
26194                 return dataView.getUint32(dataOffset, littleEndian);
26195             },
26196             size: 4
26197         },
26198         // rational = two long values, first is numerator, second is denominator:
26199         5: {
26200             getValue: function (dataView, dataOffset, littleEndian) {
26201                 return dataView.getUint32(dataOffset, littleEndian) /
26202                     dataView.getUint32(dataOffset + 4, littleEndian);
26203             },
26204             size: 8
26205         },
26206         // slong, 32 bit signed int:
26207         9: {
26208             getValue: function (dataView, dataOffset, littleEndian) {
26209                 return dataView.getInt32(dataOffset, littleEndian);
26210             },
26211             size: 4
26212         },
26213         // srational, two slongs, first is numerator, second is denominator:
26214         10: {
26215             getValue: function (dataView, dataOffset, littleEndian) {
26216                 return dataView.getInt32(dataOffset, littleEndian) /
26217                     dataView.getInt32(dataOffset + 4, littleEndian);
26218             },
26219             size: 8
26220         }
26221     },
26222     
26223     footer : {
26224         STANDARD : [
26225             {
26226                 tag : 'div',
26227                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26228                 action : 'rotate-left',
26229                 cn : [
26230                     {
26231                         tag : 'button',
26232                         cls : 'btn btn-default',
26233                         html : '<i class="fa fa-undo"></i>'
26234                     }
26235                 ]
26236             },
26237             {
26238                 tag : 'div',
26239                 cls : 'btn-group roo-upload-cropbox-picture',
26240                 action : 'picture',
26241                 cn : [
26242                     {
26243                         tag : 'button',
26244                         cls : 'btn btn-default',
26245                         html : '<i class="fa fa-picture-o"></i>'
26246                     }
26247                 ]
26248             },
26249             {
26250                 tag : 'div',
26251                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26252                 action : 'rotate-right',
26253                 cn : [
26254                     {
26255                         tag : 'button',
26256                         cls : 'btn btn-default',
26257                         html : '<i class="fa fa-repeat"></i>'
26258                     }
26259                 ]
26260             }
26261         ],
26262         DOCUMENT : [
26263             {
26264                 tag : 'div',
26265                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26266                 action : 'rotate-left',
26267                 cn : [
26268                     {
26269                         tag : 'button',
26270                         cls : 'btn btn-default',
26271                         html : '<i class="fa fa-undo"></i>'
26272                     }
26273                 ]
26274             },
26275             {
26276                 tag : 'div',
26277                 cls : 'btn-group roo-upload-cropbox-download',
26278                 action : 'download',
26279                 cn : [
26280                     {
26281                         tag : 'button',
26282                         cls : 'btn btn-default',
26283                         html : '<i class="fa fa-download"></i>'
26284                     }
26285                 ]
26286             },
26287             {
26288                 tag : 'div',
26289                 cls : 'btn-group roo-upload-cropbox-crop',
26290                 action : 'crop',
26291                 cn : [
26292                     {
26293                         tag : 'button',
26294                         cls : 'btn btn-default',
26295                         html : '<i class="fa fa-crop"></i>'
26296                     }
26297                 ]
26298             },
26299             {
26300                 tag : 'div',
26301                 cls : 'btn-group roo-upload-cropbox-trash',
26302                 action : 'trash',
26303                 cn : [
26304                     {
26305                         tag : 'button',
26306                         cls : 'btn btn-default',
26307                         html : '<i class="fa fa-trash"></i>'
26308                     }
26309                 ]
26310             },
26311             {
26312                 tag : 'div',
26313                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26314                 action : 'rotate-right',
26315                 cn : [
26316                     {
26317                         tag : 'button',
26318                         cls : 'btn btn-default',
26319                         html : '<i class="fa fa-repeat"></i>'
26320                     }
26321                 ]
26322             }
26323         ],
26324         ROTATOR : [
26325             {
26326                 tag : 'div',
26327                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26328                 action : 'rotate-left',
26329                 cn : [
26330                     {
26331                         tag : 'button',
26332                         cls : 'btn btn-default',
26333                         html : '<i class="fa fa-undo"></i>'
26334                     }
26335                 ]
26336             },
26337             {
26338                 tag : 'div',
26339                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26340                 action : 'rotate-right',
26341                 cn : [
26342                     {
26343                         tag : 'button',
26344                         cls : 'btn btn-default',
26345                         html : '<i class="fa fa-repeat"></i>'
26346                     }
26347                 ]
26348             }
26349         ]
26350     }
26351 });
26352
26353 /*
26354 * Licence: LGPL
26355 */
26356
26357 /**
26358  * @class Roo.bootstrap.DocumentManager
26359  * @extends Roo.bootstrap.Component
26360  * Bootstrap DocumentManager class
26361  * @cfg {String} paramName default 'imageUpload'
26362  * @cfg {String} method default POST
26363  * @cfg {String} url action url
26364  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26365  * @cfg {Boolean} multiple multiple upload default true
26366  * @cfg {Number} thumbSize default 300
26367  * @cfg {String} fieldLabel
26368  * @cfg {Number} labelWidth default 4
26369  * @cfg {String} labelAlign (left|top) default left
26370  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26371  * 
26372  * @constructor
26373  * Create a new DocumentManager
26374  * @param {Object} config The config object
26375  */
26376
26377 Roo.bootstrap.DocumentManager = function(config){
26378     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26379     
26380     this.addEvents({
26381         /**
26382          * @event initial
26383          * Fire when initial the DocumentManager
26384          * @param {Roo.bootstrap.DocumentManager} this
26385          */
26386         "initial" : true,
26387         /**
26388          * @event inspect
26389          * inspect selected file
26390          * @param {Roo.bootstrap.DocumentManager} this
26391          * @param {File} file
26392          */
26393         "inspect" : true,
26394         /**
26395          * @event exception
26396          * Fire when xhr load exception
26397          * @param {Roo.bootstrap.DocumentManager} this
26398          * @param {XMLHttpRequest} xhr
26399          */
26400         "exception" : true,
26401         /**
26402          * @event prepare
26403          * prepare the form data
26404          * @param {Roo.bootstrap.DocumentManager} this
26405          * @param {Object} formData
26406          */
26407         "prepare" : true,
26408         /**
26409          * @event remove
26410          * Fire when remove the file
26411          * @param {Roo.bootstrap.DocumentManager} this
26412          * @param {Object} file
26413          */
26414         "remove" : true,
26415         /**
26416          * @event refresh
26417          * Fire after refresh the file
26418          * @param {Roo.bootstrap.DocumentManager} this
26419          */
26420         "refresh" : true,
26421         /**
26422          * @event click
26423          * Fire after click the image
26424          * @param {Roo.bootstrap.DocumentManager} this
26425          * @param {Object} file
26426          */
26427         "click" : true,
26428         /**
26429          * @event edit
26430          * Fire when upload a image and editable set to true
26431          * @param {Roo.bootstrap.DocumentManager} this
26432          * @param {Object} file
26433          */
26434         "edit" : true,
26435         /**
26436          * @event beforeselectfile
26437          * Fire before select file
26438          * @param {Roo.bootstrap.DocumentManager} this
26439          */
26440         "beforeselectfile" : true,
26441         /**
26442          * @event process
26443          * Fire before process file
26444          * @param {Roo.bootstrap.DocumentManager} this
26445          * @param {Object} file
26446          */
26447         "process" : true
26448         
26449     });
26450 };
26451
26452 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26453     
26454     boxes : 0,
26455     inputName : '',
26456     thumbSize : 300,
26457     multiple : true,
26458     files : [],
26459     method : 'POST',
26460     url : '',
26461     paramName : 'imageUpload',
26462     fieldLabel : '',
26463     labelWidth : 4,
26464     labelAlign : 'left',
26465     editable : true,
26466     delegates : [],
26467     
26468     
26469     xhr : false, 
26470     
26471     getAutoCreate : function()
26472     {   
26473         var managerWidget = {
26474             tag : 'div',
26475             cls : 'roo-document-manager',
26476             cn : [
26477                 {
26478                     tag : 'input',
26479                     cls : 'roo-document-manager-selector',
26480                     type : 'file'
26481                 },
26482                 {
26483                     tag : 'div',
26484                     cls : 'roo-document-manager-uploader',
26485                     cn : [
26486                         {
26487                             tag : 'div',
26488                             cls : 'roo-document-manager-upload-btn',
26489                             html : '<i class="fa fa-plus"></i>'
26490                         }
26491                     ]
26492                     
26493                 }
26494             ]
26495         };
26496         
26497         var content = [
26498             {
26499                 tag : 'div',
26500                 cls : 'column col-md-12',
26501                 cn : managerWidget
26502             }
26503         ];
26504         
26505         if(this.fieldLabel.length){
26506             
26507             content = [
26508                 {
26509                     tag : 'div',
26510                     cls : 'column col-md-12',
26511                     html : this.fieldLabel
26512                 },
26513                 {
26514                     tag : 'div',
26515                     cls : 'column col-md-12',
26516                     cn : managerWidget
26517                 }
26518             ];
26519
26520             if(this.labelAlign == 'left'){
26521                 content = [
26522                     {
26523                         tag : 'div',
26524                         cls : 'column col-md-' + this.labelWidth,
26525                         html : this.fieldLabel
26526                     },
26527                     {
26528                         tag : 'div',
26529                         cls : 'column col-md-' + (12 - this.labelWidth),
26530                         cn : managerWidget
26531                     }
26532                 ];
26533                 
26534             }
26535         }
26536         
26537         var cfg = {
26538             tag : 'div',
26539             cls : 'row clearfix',
26540             cn : content
26541         };
26542         
26543         return cfg;
26544         
26545     },
26546     
26547     initEvents : function()
26548     {
26549         this.managerEl = this.el.select('.roo-document-manager', true).first();
26550         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26551         
26552         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26553         this.selectorEl.hide();
26554         
26555         if(this.multiple){
26556             this.selectorEl.attr('multiple', 'multiple');
26557         }
26558         
26559         this.selectorEl.on('change', this.onFileSelected, this);
26560         
26561         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26562         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26563         
26564         this.uploader.on('click', this.onUploaderClick, this);
26565         
26566         this.renderProgressDialog();
26567         
26568         var _this = this;
26569         
26570         window.addEventListener("resize", function() { _this.refresh(); } );
26571         
26572         this.fireEvent('initial', this);
26573     },
26574     
26575     renderProgressDialog : function()
26576     {
26577         var _this = this;
26578         
26579         this.progressDialog = new Roo.bootstrap.Modal({
26580             cls : 'roo-document-manager-progress-dialog',
26581             allow_close : false,
26582             title : '',
26583             buttons : [
26584                 {
26585                     name  :'cancel',
26586                     weight : 'danger',
26587                     html : 'Cancel'
26588                 }
26589             ], 
26590             listeners : { 
26591                 btnclick : function() {
26592                     _this.uploadCancel();
26593                     this.hide();
26594                 }
26595             }
26596         });
26597          
26598         this.progressDialog.render(Roo.get(document.body));
26599          
26600         this.progress = new Roo.bootstrap.Progress({
26601             cls : 'roo-document-manager-progress',
26602             active : true,
26603             striped : true
26604         });
26605         
26606         this.progress.render(this.progressDialog.getChildContainer());
26607         
26608         this.progressBar = new Roo.bootstrap.ProgressBar({
26609             cls : 'roo-document-manager-progress-bar',
26610             aria_valuenow : 0,
26611             aria_valuemin : 0,
26612             aria_valuemax : 12,
26613             panel : 'success'
26614         });
26615         
26616         this.progressBar.render(this.progress.getChildContainer());
26617     },
26618     
26619     onUploaderClick : function(e)
26620     {
26621         e.preventDefault();
26622      
26623         if(this.fireEvent('beforeselectfile', this) != false){
26624             this.selectorEl.dom.click();
26625         }
26626         
26627     },
26628     
26629     onFileSelected : function(e)
26630     {
26631         e.preventDefault();
26632         
26633         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26634             return;
26635         }
26636         
26637         Roo.each(this.selectorEl.dom.files, function(file){
26638             if(this.fireEvent('inspect', this, file) != false){
26639                 this.files.push(file);
26640             }
26641         }, this);
26642         
26643         this.queue();
26644         
26645     },
26646     
26647     queue : function()
26648     {
26649         this.selectorEl.dom.value = '';
26650         
26651         if(!this.files.length){
26652             return;
26653         }
26654         
26655         if(this.boxes > 0 && this.files.length > this.boxes){
26656             this.files = this.files.slice(0, this.boxes);
26657         }
26658         
26659         this.uploader.show();
26660         
26661         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26662             this.uploader.hide();
26663         }
26664         
26665         var _this = this;
26666         
26667         var files = [];
26668         
26669         var docs = [];
26670         
26671         Roo.each(this.files, function(file){
26672             
26673             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26674                 var f = this.renderPreview(file);
26675                 files.push(f);
26676                 return;
26677             }
26678             
26679             if(file.type.indexOf('image') != -1){
26680                 this.delegates.push(
26681                     (function(){
26682                         _this.process(file);
26683                     }).createDelegate(this)
26684                 );
26685         
26686                 return;
26687             }
26688             
26689             docs.push(
26690                 (function(){
26691                     _this.process(file);
26692                 }).createDelegate(this)
26693             );
26694             
26695         }, this);
26696         
26697         this.files = files;
26698         
26699         this.delegates = this.delegates.concat(docs);
26700         
26701         if(!this.delegates.length){
26702             this.refresh();
26703             return;
26704         }
26705         
26706         this.progressBar.aria_valuemax = this.delegates.length;
26707         
26708         this.arrange();
26709         
26710         return;
26711     },
26712     
26713     arrange : function()
26714     {
26715         if(!this.delegates.length){
26716             this.progressDialog.hide();
26717             this.refresh();
26718             return;
26719         }
26720         
26721         var delegate = this.delegates.shift();
26722         
26723         this.progressDialog.show();
26724         
26725         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
26726         
26727         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
26728         
26729         delegate();
26730     },
26731     
26732     refresh : function()
26733     {
26734         this.uploader.show();
26735         
26736         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26737             this.uploader.hide();
26738         }
26739         
26740         Roo.isTouch ? this.closable(false) : this.closable(true);
26741         
26742         this.fireEvent('refresh', this);
26743     },
26744     
26745     onRemove : function(e, el, o)
26746     {
26747         e.preventDefault();
26748         
26749         this.fireEvent('remove', this, o);
26750         
26751     },
26752     
26753     remove : function(o)
26754     {
26755         var files = [];
26756         
26757         Roo.each(this.files, function(file){
26758             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
26759                 files.push(file);
26760                 return;
26761             }
26762
26763             o.target.remove();
26764
26765         }, this);
26766         
26767         this.files = files;
26768         
26769         this.refresh();
26770     },
26771     
26772     clear : function()
26773     {
26774         Roo.each(this.files, function(file){
26775             if(!file.target){
26776                 return;
26777             }
26778             
26779             file.target.remove();
26780
26781         }, this);
26782         
26783         this.files = [];
26784         
26785         this.refresh();
26786     },
26787     
26788     onClick : function(e, el, o)
26789     {
26790         e.preventDefault();
26791         
26792         this.fireEvent('click', this, o);
26793         
26794     },
26795     
26796     closable : function(closable)
26797     {
26798         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
26799             
26800             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26801             
26802             if(closable){
26803                 el.show();
26804                 return;
26805             }
26806             
26807             el.hide();
26808             
26809         }, this);
26810     },
26811     
26812     xhrOnLoad : function(xhr)
26813     {
26814         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26815             el.remove();
26816         }, this);
26817         
26818         if (xhr.readyState !== 4) {
26819             this.arrange();
26820             this.fireEvent('exception', this, xhr);
26821             return;
26822         }
26823
26824         var response = Roo.decode(xhr.responseText);
26825         
26826         if(!response.success){
26827             this.arrange();
26828             this.fireEvent('exception', this, xhr);
26829             return;
26830         }
26831         
26832         var file = this.renderPreview(response.data);
26833         
26834         this.files.push(file);
26835         
26836         this.arrange();
26837         
26838     },
26839     
26840     xhrOnError : function(xhr)
26841     {
26842         Roo.log('xhr on error');
26843         
26844         var response = Roo.decode(xhr.responseText);
26845           
26846         Roo.log(response);
26847         
26848         this.arrange();
26849     },
26850     
26851     process : function(file)
26852     {
26853         if(this.fireEvent('process', this, file) !== false){
26854             if(this.editable && file.type.indexOf('image') != -1){
26855                 this.fireEvent('edit', this, file);
26856                 return;
26857             }
26858
26859             this.uploadStart(file, false);
26860
26861             return;
26862         }
26863         
26864     },
26865     
26866     uploadStart : function(file, crop)
26867     {
26868         this.xhr = new XMLHttpRequest();
26869         
26870         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
26871             this.arrange();
26872             return;
26873         }
26874         
26875         file.xhr = this.xhr;
26876             
26877         this.managerEl.createChild({
26878             tag : 'div',
26879             cls : 'roo-document-manager-loading',
26880             cn : [
26881                 {
26882                     tag : 'div',
26883                     tooltip : file.name,
26884                     cls : 'roo-document-manager-thumb',
26885                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
26886                 }
26887             ]
26888
26889         });
26890
26891         this.xhr.open(this.method, this.url, true);
26892         
26893         var headers = {
26894             "Accept": "application/json",
26895             "Cache-Control": "no-cache",
26896             "X-Requested-With": "XMLHttpRequest"
26897         };
26898         
26899         for (var headerName in headers) {
26900             var headerValue = headers[headerName];
26901             if (headerValue) {
26902                 this.xhr.setRequestHeader(headerName, headerValue);
26903             }
26904         }
26905         
26906         var _this = this;
26907         
26908         this.xhr.onload = function()
26909         {
26910             _this.xhrOnLoad(_this.xhr);
26911         }
26912         
26913         this.xhr.onerror = function()
26914         {
26915             _this.xhrOnError(_this.xhr);
26916         }
26917         
26918         var formData = new FormData();
26919
26920         formData.append('returnHTML', 'NO');
26921         
26922         if(crop){
26923             formData.append('crop', crop);
26924         }
26925         
26926         formData.append(this.paramName, file, file.name);
26927         
26928         if(this.fireEvent('prepare', this, formData) != false){
26929             this.xhr.send(formData);
26930         };
26931     },
26932     
26933     uploadCancel : function()
26934     {
26935         if (this.xhr) {
26936             this.xhr.abort();
26937         }
26938         
26939         
26940         this.delegates = [];
26941         
26942         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
26943             el.remove();
26944         }, this);
26945         
26946         this.arrange();
26947     },
26948     
26949     renderPreview : function(file)
26950     {
26951         if(typeof(file.target) != 'undefined' && file.target){
26952             return file;
26953         }
26954         
26955         var previewEl = this.managerEl.createChild({
26956             tag : 'div',
26957             cls : 'roo-document-manager-preview',
26958             cn : [
26959                 {
26960                     tag : 'div',
26961                     tooltip : file.filename,
26962                     cls : 'roo-document-manager-thumb',
26963                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
26964                 },
26965                 {
26966                     tag : 'button',
26967                     cls : 'close',
26968                     html : '<i class="fa fa-times-circle"></i>'
26969                 }
26970             ]
26971         });
26972
26973         var close = previewEl.select('button.close', true).first();
26974
26975         close.on('click', this.onRemove, this, file);
26976
26977         file.target = previewEl;
26978
26979         var image = previewEl.select('img', true).first();
26980         
26981         var _this = this;
26982         
26983         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
26984         
26985         image.on('click', this.onClick, this, file);
26986         
26987         return file;
26988         
26989     },
26990     
26991     onPreviewLoad : function(file, image)
26992     {
26993         if(typeof(file.target) == 'undefined' || !file.target){
26994             return;
26995         }
26996         
26997         var width = image.dom.naturalWidth || image.dom.width;
26998         var height = image.dom.naturalHeight || image.dom.height;
26999         
27000         if(width > height){
27001             file.target.addClass('wide');
27002             return;
27003         }
27004         
27005         file.target.addClass('tall');
27006         return;
27007         
27008     },
27009     
27010     uploadFromSource : function(file, crop)
27011     {
27012         this.xhr = new XMLHttpRequest();
27013         
27014         this.managerEl.createChild({
27015             tag : 'div',
27016             cls : 'roo-document-manager-loading',
27017             cn : [
27018                 {
27019                     tag : 'div',
27020                     tooltip : file.name,
27021                     cls : 'roo-document-manager-thumb',
27022                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27023                 }
27024             ]
27025
27026         });
27027
27028         this.xhr.open(this.method, this.url, true);
27029         
27030         var headers = {
27031             "Accept": "application/json",
27032             "Cache-Control": "no-cache",
27033             "X-Requested-With": "XMLHttpRequest"
27034         };
27035         
27036         for (var headerName in headers) {
27037             var headerValue = headers[headerName];
27038             if (headerValue) {
27039                 this.xhr.setRequestHeader(headerName, headerValue);
27040             }
27041         }
27042         
27043         var _this = this;
27044         
27045         this.xhr.onload = function()
27046         {
27047             _this.xhrOnLoad(_this.xhr);
27048         }
27049         
27050         this.xhr.onerror = function()
27051         {
27052             _this.xhrOnError(_this.xhr);
27053         }
27054         
27055         var formData = new FormData();
27056
27057         formData.append('returnHTML', 'NO');
27058         
27059         formData.append('crop', crop);
27060         
27061         if(typeof(file.filename) != 'undefined'){
27062             formData.append('filename', file.filename);
27063         }
27064         
27065         if(typeof(file.mimetype) != 'undefined'){
27066             formData.append('mimetype', file.mimetype);
27067         }
27068         
27069         if(this.fireEvent('prepare', this, formData) != false){
27070             this.xhr.send(formData);
27071         };
27072     }
27073 });
27074
27075 /*
27076 * Licence: LGPL
27077 */
27078
27079 /**
27080  * @class Roo.bootstrap.DocumentViewer
27081  * @extends Roo.bootstrap.Component
27082  * Bootstrap DocumentViewer class
27083  * 
27084  * @constructor
27085  * Create a new DocumentViewer
27086  * @param {Object} config The config object
27087  */
27088
27089 Roo.bootstrap.DocumentViewer = function(config){
27090     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27091     
27092     this.addEvents({
27093         /**
27094          * @event initial
27095          * Fire after initEvent
27096          * @param {Roo.bootstrap.DocumentViewer} this
27097          */
27098         "initial" : true,
27099         /**
27100          * @event click
27101          * Fire after click
27102          * @param {Roo.bootstrap.DocumentViewer} this
27103          */
27104         "click" : true,
27105         /**
27106          * @event trash
27107          * Fire after trash button
27108          * @param {Roo.bootstrap.DocumentViewer} this
27109          */
27110         "trash" : true
27111         
27112     });
27113 };
27114
27115 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27116     
27117     getAutoCreate : function()
27118     {
27119         var cfg = {
27120             tag : 'div',
27121             cls : 'roo-document-viewer',
27122             cn : [
27123                 {
27124                     tag : 'div',
27125                     cls : 'roo-document-viewer-body',
27126                     cn : [
27127                         {
27128                             tag : 'div',
27129                             cls : 'roo-document-viewer-thumb',
27130                             cn : [
27131                                 {
27132                                     tag : 'img',
27133                                     cls : 'roo-document-viewer-image'
27134                                 }
27135                             ]
27136                         }
27137                     ]
27138                 },
27139                 {
27140                     tag : 'div',
27141                     cls : 'roo-document-viewer-footer',
27142                     cn : {
27143                         tag : 'div',
27144                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27145                         cn : [
27146                             {
27147                                 tag : 'div',
27148                                 cls : 'btn-group',
27149                                 cn : [
27150                                     {
27151                                         tag : 'button',
27152                                         cls : 'btn btn-default roo-document-viewer-trash',
27153                                         html : '<i class="fa fa-trash"></i>'
27154                                     }
27155                                 ]
27156                             }
27157                         ]
27158                     }
27159                 }
27160             ]
27161         };
27162         
27163         return cfg;
27164     },
27165     
27166     initEvents : function()
27167     {
27168         
27169         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27170         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27171         
27172         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27173         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27174         
27175         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27176         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27177         
27178         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27179         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27180         
27181         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27182         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27183         
27184         this.bodyEl.on('click', this.onClick, this);
27185         
27186         this.trashBtn.on('click', this.onTrash, this);
27187         
27188     },
27189     
27190     initial : function()
27191     {
27192 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27193         
27194         
27195         this.fireEvent('initial', this);
27196         
27197     },
27198     
27199     onClick : function(e)
27200     {
27201         e.preventDefault();
27202         
27203         this.fireEvent('click', this);
27204     },
27205     
27206     onTrash : function(e)
27207     {
27208         e.preventDefault();
27209         
27210         this.fireEvent('trash', this);
27211     }
27212     
27213 });
27214 /*
27215  * - LGPL
27216  *
27217  * nav progress bar
27218  * 
27219  */
27220
27221 /**
27222  * @class Roo.bootstrap.NavProgressBar
27223  * @extends Roo.bootstrap.Component
27224  * Bootstrap NavProgressBar class
27225  * 
27226  * @constructor
27227  * Create a new nav progress bar
27228  * @param {Object} config The config object
27229  */
27230
27231 Roo.bootstrap.NavProgressBar = function(config){
27232     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27233
27234     this.bullets = this.bullets || [];
27235    
27236 //    Roo.bootstrap.NavProgressBar.register(this);
27237      this.addEvents({
27238         /**
27239              * @event changed
27240              * Fires when the active item changes
27241              * @param {Roo.bootstrap.NavProgressBar} this
27242              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27243              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27244          */
27245         'changed': true
27246      });
27247     
27248 };
27249
27250 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27251     
27252     bullets : [],
27253     barItems : [],
27254     
27255     getAutoCreate : function()
27256     {
27257         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27258         
27259         cfg = {
27260             tag : 'div',
27261             cls : 'roo-navigation-bar-group',
27262             cn : [
27263                 {
27264                     tag : 'div',
27265                     cls : 'roo-navigation-top-bar'
27266                 },
27267                 {
27268                     tag : 'div',
27269                     cls : 'roo-navigation-bullets-bar',
27270                     cn : [
27271                         {
27272                             tag : 'ul',
27273                             cls : 'roo-navigation-bar'
27274                         }
27275                     ]
27276                 },
27277                 
27278                 {
27279                     tag : 'div',
27280                     cls : 'roo-navigation-bottom-bar'
27281                 }
27282             ]
27283             
27284         };
27285         
27286         return cfg;
27287         
27288     },
27289     
27290     initEvents: function() 
27291     {
27292         
27293     },
27294     
27295     onRender : function(ct, position) 
27296     {
27297         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27298         
27299         if(this.bullets.length){
27300             Roo.each(this.bullets, function(b){
27301                this.addItem(b);
27302             }, this);
27303         }
27304         
27305         this.format();
27306         
27307     },
27308     
27309     addItem : function(cfg)
27310     {
27311         var item = new Roo.bootstrap.NavProgressItem(cfg);
27312         
27313         item.parentId = this.id;
27314         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27315         
27316         if(cfg.html){
27317             var top = new Roo.bootstrap.Element({
27318                 tag : 'div',
27319                 cls : 'roo-navigation-bar-text'
27320             });
27321             
27322             var bottom = new Roo.bootstrap.Element({
27323                 tag : 'div',
27324                 cls : 'roo-navigation-bar-text'
27325             });
27326             
27327             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27328             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27329             
27330             var topText = new Roo.bootstrap.Element({
27331                 tag : 'span',
27332                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27333             });
27334             
27335             var bottomText = new Roo.bootstrap.Element({
27336                 tag : 'span',
27337                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27338             });
27339             
27340             topText.onRender(top.el, null);
27341             bottomText.onRender(bottom.el, null);
27342             
27343             item.topEl = top;
27344             item.bottomEl = bottom;
27345         }
27346         
27347         this.barItems.push(item);
27348         
27349         return item;
27350     },
27351     
27352     getActive : function()
27353     {
27354         var active = false;
27355         
27356         Roo.each(this.barItems, function(v){
27357             
27358             if (!v.isActive()) {
27359                 return;
27360             }
27361             
27362             active = v;
27363             return false;
27364             
27365         });
27366         
27367         return active;
27368     },
27369     
27370     setActiveItem : function(item)
27371     {
27372         var prev = false;
27373         
27374         Roo.each(this.barItems, function(v){
27375             if (v.rid == item.rid) {
27376                 return ;
27377             }
27378             
27379             if (v.isActive()) {
27380                 v.setActive(false);
27381                 prev = v;
27382             }
27383         });
27384
27385         item.setActive(true);
27386         
27387         this.fireEvent('changed', this, item, prev);
27388     },
27389     
27390     getBarItem: function(rid)
27391     {
27392         var ret = false;
27393         
27394         Roo.each(this.barItems, function(e) {
27395             if (e.rid != rid) {
27396                 return;
27397             }
27398             
27399             ret =  e;
27400             return false;
27401         });
27402         
27403         return ret;
27404     },
27405     
27406     indexOfItem : function(item)
27407     {
27408         var index = false;
27409         
27410         Roo.each(this.barItems, function(v, i){
27411             
27412             if (v.rid != item.rid) {
27413                 return;
27414             }
27415             
27416             index = i;
27417             return false
27418         });
27419         
27420         return index;
27421     },
27422     
27423     setActiveNext : function()
27424     {
27425         var i = this.indexOfItem(this.getActive());
27426         
27427         if (i > this.barItems.length) {
27428             return;
27429         }
27430         
27431         this.setActiveItem(this.barItems[i+1]);
27432     },
27433     
27434     setActivePrev : function()
27435     {
27436         var i = this.indexOfItem(this.getActive());
27437         
27438         if (i  < 1) {
27439             return;
27440         }
27441         
27442         this.setActiveItem(this.barItems[i-1]);
27443     },
27444     
27445     format : function()
27446     {
27447         if(!this.barItems.length){
27448             return;
27449         }
27450      
27451         var width = 100 / this.barItems.length;
27452         
27453         Roo.each(this.barItems, function(i){
27454             i.el.setStyle('width', width + '%');
27455             i.topEl.el.setStyle('width', width + '%');
27456             i.bottomEl.el.setStyle('width', width + '%');
27457         }, this);
27458         
27459     }
27460     
27461 });
27462 /*
27463  * - LGPL
27464  *
27465  * Nav Progress Item
27466  * 
27467  */
27468
27469 /**
27470  * @class Roo.bootstrap.NavProgressItem
27471  * @extends Roo.bootstrap.Component
27472  * Bootstrap NavProgressItem class
27473  * @cfg {String} rid the reference id
27474  * @cfg {Boolean} active (true|false) Is item active default false
27475  * @cfg {Boolean} disabled (true|false) Is item active default false
27476  * @cfg {String} html
27477  * @cfg {String} position (top|bottom) text position default bottom
27478  * @cfg {String} icon show icon instead of number
27479  * 
27480  * @constructor
27481  * Create a new NavProgressItem
27482  * @param {Object} config The config object
27483  */
27484 Roo.bootstrap.NavProgressItem = function(config){
27485     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27486     this.addEvents({
27487         // raw events
27488         /**
27489          * @event click
27490          * The raw click event for the entire grid.
27491          * @param {Roo.bootstrap.NavProgressItem} this
27492          * @param {Roo.EventObject} e
27493          */
27494         "click" : true
27495     });
27496    
27497 };
27498
27499 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27500     
27501     rid : '',
27502     active : false,
27503     disabled : false,
27504     html : '',
27505     position : 'bottom',
27506     icon : false,
27507     
27508     getAutoCreate : function()
27509     {
27510         var iconCls = 'roo-navigation-bar-item-icon';
27511         
27512         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27513         
27514         var cfg = {
27515             tag: 'li',
27516             cls: 'roo-navigation-bar-item',
27517             cn : [
27518                 {
27519                     tag : 'i',
27520                     cls : iconCls
27521                 }
27522             ]
27523         };
27524         
27525         if(this.active){
27526             cfg.cls += ' active';
27527         }
27528         if(this.disabled){
27529             cfg.cls += ' disabled';
27530         }
27531         
27532         return cfg;
27533     },
27534     
27535     disable : function()
27536     {
27537         this.setDisabled(true);
27538     },
27539     
27540     enable : function()
27541     {
27542         this.setDisabled(false);
27543     },
27544     
27545     initEvents: function() 
27546     {
27547         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27548         
27549         this.iconEl.on('click', this.onClick, this);
27550     },
27551     
27552     onClick : function(e)
27553     {
27554         e.preventDefault();
27555         
27556         if(this.disabled){
27557             return;
27558         }
27559         
27560         if(this.fireEvent('click', this, e) === false){
27561             return;
27562         };
27563         
27564         this.parent().setActiveItem(this);
27565     },
27566     
27567     isActive: function () 
27568     {
27569         return this.active;
27570     },
27571     
27572     setActive : function(state)
27573     {
27574         if(this.active == state){
27575             return;
27576         }
27577         
27578         this.active = state;
27579         
27580         if (state) {
27581             this.el.addClass('active');
27582             return;
27583         }
27584         
27585         this.el.removeClass('active');
27586         
27587         return;
27588     },
27589     
27590     setDisabled : function(state)
27591     {
27592         if(this.disabled == state){
27593             return;
27594         }
27595         
27596         this.disabled = state;
27597         
27598         if (state) {
27599             this.el.addClass('disabled');
27600             return;
27601         }
27602         
27603         this.el.removeClass('disabled');
27604     },
27605     
27606     tooltipEl : function()
27607     {
27608         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27609     }
27610 });
27611  
27612
27613  /*
27614  * - LGPL
27615  *
27616  * FieldLabel
27617  * 
27618  */
27619
27620 /**
27621  * @class Roo.bootstrap.FieldLabel
27622  * @extends Roo.bootstrap.Component
27623  * Bootstrap FieldLabel class
27624  * @cfg {String} html contents of the element
27625  * @cfg {String} tag tag of the element default label
27626  * @cfg {String} cls class of the element
27627  * @cfg {String} target label target 
27628  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27629  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27630  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27631  * @cfg {String} iconTooltip default "This field is required"
27632  * 
27633  * @constructor
27634  * Create a new FieldLabel
27635  * @param {Object} config The config object
27636  */
27637
27638 Roo.bootstrap.FieldLabel = function(config){
27639     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27640     
27641     this.addEvents({
27642             /**
27643              * @event invalid
27644              * Fires after the field has been marked as invalid.
27645              * @param {Roo.form.FieldLabel} this
27646              * @param {String} msg The validation message
27647              */
27648             invalid : true,
27649             /**
27650              * @event valid
27651              * Fires after the field has been validated with no errors.
27652              * @param {Roo.form.FieldLabel} this
27653              */
27654             valid : true
27655         });
27656 };
27657
27658 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27659     
27660     tag: 'label',
27661     cls: '',
27662     html: '',
27663     target: '',
27664     allowBlank : true,
27665     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
27666     validClass : 'text-success fa fa-lg fa-check',
27667     iconTooltip : 'This field is required',
27668     
27669     getAutoCreate : function(){
27670         
27671         var cfg = {
27672             tag : this.tag,
27673             cls : 'roo-bootstrap-field-label ' + this.cls,
27674             for : this.target,
27675             cn : [
27676                 {
27677                     tag : 'i',
27678                     cls : '',
27679                     tooltip : this.iconTooltip
27680                 },
27681                 {
27682                     tag : 'span',
27683                     html : this.html
27684                 }
27685             ] 
27686         };
27687         
27688         return cfg;
27689     },
27690     
27691     initEvents: function() 
27692     {
27693         Roo.bootstrap.Element.superclass.initEvents.call(this);
27694         
27695         this.iconEl = this.el.select('i', true).first();
27696         
27697         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
27698         
27699         Roo.bootstrap.FieldLabel.register(this);
27700     },
27701     
27702     /**
27703      * Mark this field as valid
27704      */
27705     markValid : function()
27706     {
27707         this.iconEl.show();
27708         
27709         this.iconEl.removeClass(this.invalidClass);
27710         
27711         this.iconEl.addClass(this.validClass);
27712         
27713         this.fireEvent('valid', this);
27714     },
27715     
27716     /**
27717      * Mark this field as invalid
27718      * @param {String} msg The validation message
27719      */
27720     markInvalid : function(msg)
27721     {
27722         this.iconEl.show();
27723         
27724         this.iconEl.removeClass(this.validClass);
27725         
27726         this.iconEl.addClass(this.invalidClass);
27727         
27728         this.fireEvent('invalid', this, msg);
27729     }
27730     
27731    
27732 });
27733
27734 Roo.apply(Roo.bootstrap.FieldLabel, {
27735     
27736     groups: {},
27737     
27738      /**
27739     * register a FieldLabel Group
27740     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
27741     */
27742     register : function(label)
27743     {
27744         if(this.groups.hasOwnProperty(label.target)){
27745             return;
27746         }
27747      
27748         this.groups[label.target] = label;
27749         
27750     },
27751     /**
27752     * fetch a FieldLabel Group based on the target
27753     * @param {string} target
27754     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
27755     */
27756     get: function(target) {
27757         if (typeof(this.groups[target]) == 'undefined') {
27758             return false;
27759         }
27760         
27761         return this.groups[target] ;
27762     }
27763 });
27764
27765  
27766
27767  /*
27768  * - LGPL
27769  *
27770  * page DateSplitField.
27771  * 
27772  */
27773
27774
27775 /**
27776  * @class Roo.bootstrap.DateSplitField
27777  * @extends Roo.bootstrap.Component
27778  * Bootstrap DateSplitField class
27779  * @cfg {string} fieldLabel - the label associated
27780  * @cfg {Number} labelWidth set the width of label (0-12)
27781  * @cfg {String} labelAlign (top|left)
27782  * @cfg {Boolean} dayAllowBlank (true|false) default false
27783  * @cfg {Boolean} monthAllowBlank (true|false) default false
27784  * @cfg {Boolean} yearAllowBlank (true|false) default false
27785  * @cfg {string} dayPlaceholder 
27786  * @cfg {string} monthPlaceholder
27787  * @cfg {string} yearPlaceholder
27788  * @cfg {string} dayFormat default 'd'
27789  * @cfg {string} monthFormat default 'm'
27790  * @cfg {string} yearFormat default 'Y'
27791
27792  *     
27793  * @constructor
27794  * Create a new DateSplitField
27795  * @param {Object} config The config object
27796  */
27797
27798 Roo.bootstrap.DateSplitField = function(config){
27799     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
27800     
27801     this.addEvents({
27802         // raw events
27803          /**
27804          * @event years
27805          * getting the data of years
27806          * @param {Roo.bootstrap.DateSplitField} this
27807          * @param {Object} years
27808          */
27809         "years" : true,
27810         /**
27811          * @event days
27812          * getting the data of days
27813          * @param {Roo.bootstrap.DateSplitField} this
27814          * @param {Object} days
27815          */
27816         "days" : true,
27817         /**
27818          * @event invalid
27819          * Fires after the field has been marked as invalid.
27820          * @param {Roo.form.Field} this
27821          * @param {String} msg The validation message
27822          */
27823         invalid : true,
27824        /**
27825          * @event valid
27826          * Fires after the field has been validated with no errors.
27827          * @param {Roo.form.Field} this
27828          */
27829         valid : true
27830     });
27831 };
27832
27833 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
27834     
27835     fieldLabel : '',
27836     labelAlign : 'top',
27837     labelWidth : 3,
27838     dayAllowBlank : false,
27839     monthAllowBlank : false,
27840     yearAllowBlank : false,
27841     dayPlaceholder : '',
27842     monthPlaceholder : '',
27843     yearPlaceholder : '',
27844     dayFormat : 'd',
27845     monthFormat : 'm',
27846     yearFormat : 'Y',
27847     isFormField : true,
27848     
27849     getAutoCreate : function()
27850     {
27851         var cfg = {
27852             tag : 'div',
27853             cls : 'row roo-date-split-field-group',
27854             cn : [
27855                 {
27856                     tag : 'input',
27857                     type : 'hidden',
27858                     cls : 'form-hidden-field roo-date-split-field-group-value',
27859                     name : this.name
27860                 }
27861             ]
27862         };
27863         
27864         if(this.fieldLabel){
27865             cfg.cn.push({
27866                 tag : 'div',
27867                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
27868                 cn : [
27869                     {
27870                         tag : 'label',
27871                         html : this.fieldLabel
27872                     }
27873                 ]
27874             });
27875         }
27876         
27877         Roo.each(['day', 'month', 'year'], function(t){
27878             cfg.cn.push({
27879                 tag : 'div',
27880                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
27881             });
27882         }, this);
27883         
27884         return cfg;
27885     },
27886     
27887     inputEl: function ()
27888     {
27889         return this.el.select('.roo-date-split-field-group-value', true).first();
27890     },
27891     
27892     onRender : function(ct, position) 
27893     {
27894         var _this = this;
27895         
27896         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27897         
27898         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
27899         
27900         this.dayField = new Roo.bootstrap.ComboBox({
27901             allowBlank : this.dayAllowBlank,
27902             alwaysQuery : true,
27903             displayField : 'value',
27904             editable : false,
27905             fieldLabel : '',
27906             forceSelection : true,
27907             mode : 'local',
27908             placeholder : this.dayPlaceholder,
27909             selectOnFocus : true,
27910             tpl : '<div class="select2-result"><b>{value}</b></div>',
27911             triggerAction : 'all',
27912             typeAhead : true,
27913             valueField : 'value',
27914             store : new Roo.data.SimpleStore({
27915                 data : (function() {    
27916                     var days = [];
27917                     _this.fireEvent('days', _this, days);
27918                     return days;
27919                 })(),
27920                 fields : [ 'value' ]
27921             }),
27922             listeners : {
27923                 select : function (_self, record, index)
27924                 {
27925                     _this.setValue(_this.getValue());
27926                 }
27927             }
27928         });
27929
27930         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
27931         
27932         this.monthField = new Roo.bootstrap.MonthField({
27933             after : '<i class=\"fa fa-calendar\"></i>',
27934             allowBlank : this.monthAllowBlank,
27935             placeholder : this.monthPlaceholder,
27936             readOnly : true,
27937             listeners : {
27938                 render : function (_self)
27939                 {
27940                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
27941                         e.preventDefault();
27942                         _self.focus();
27943                     });
27944                 },
27945                 select : function (_self, oldvalue, newvalue)
27946                 {
27947                     _this.setValue(_this.getValue());
27948                 }
27949             }
27950         });
27951         
27952         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
27953         
27954         this.yearField = new Roo.bootstrap.ComboBox({
27955             allowBlank : this.yearAllowBlank,
27956             alwaysQuery : true,
27957             displayField : 'value',
27958             editable : false,
27959             fieldLabel : '',
27960             forceSelection : true,
27961             mode : 'local',
27962             placeholder : this.yearPlaceholder,
27963             selectOnFocus : true,
27964             tpl : '<div class="select2-result"><b>{value}</b></div>',
27965             triggerAction : 'all',
27966             typeAhead : true,
27967             valueField : 'value',
27968             store : new Roo.data.SimpleStore({
27969                 data : (function() {
27970                     var years = [];
27971                     _this.fireEvent('years', _this, years);
27972                     return years;
27973                 })(),
27974                 fields : [ 'value' ]
27975             }),
27976             listeners : {
27977                 select : function (_self, record, index)
27978                 {
27979                     _this.setValue(_this.getValue());
27980                 }
27981             }
27982         });
27983
27984         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
27985     },
27986     
27987     setValue : function(v, format)
27988     {
27989         this.inputEl.dom.value = v;
27990         
27991         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
27992         
27993         var d = Date.parseDate(v, f);
27994         
27995         if(!d){
27996             this.validate();
27997             return;
27998         }
27999         
28000         this.setDay(d.format(this.dayFormat));
28001         this.setMonth(d.format(this.monthFormat));
28002         this.setYear(d.format(this.yearFormat));
28003         
28004         this.validate();
28005         
28006         return;
28007     },
28008     
28009     setDay : function(v)
28010     {
28011         this.dayField.setValue(v);
28012         this.inputEl.dom.value = this.getValue();
28013         this.validate();
28014         return;
28015     },
28016     
28017     setMonth : function(v)
28018     {
28019         this.monthField.setValue(v, true);
28020         this.inputEl.dom.value = this.getValue();
28021         this.validate();
28022         return;
28023     },
28024     
28025     setYear : function(v)
28026     {
28027         this.yearField.setValue(v);
28028         this.inputEl.dom.value = this.getValue();
28029         this.validate();
28030         return;
28031     },
28032     
28033     getDay : function()
28034     {
28035         return this.dayField.getValue();
28036     },
28037     
28038     getMonth : function()
28039     {
28040         return this.monthField.getValue();
28041     },
28042     
28043     getYear : function()
28044     {
28045         return this.yearField.getValue();
28046     },
28047     
28048     getValue : function()
28049     {
28050         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28051         
28052         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28053         
28054         return date;
28055     },
28056     
28057     reset : function()
28058     {
28059         this.setDay('');
28060         this.setMonth('');
28061         this.setYear('');
28062         this.inputEl.dom.value = '';
28063         this.validate();
28064         return;
28065     },
28066     
28067     validate : function()
28068     {
28069         var d = this.dayField.validate();
28070         var m = this.monthField.validate();
28071         var y = this.yearField.validate();
28072         
28073         var valid = true;
28074         
28075         if(
28076                 (!this.dayAllowBlank && !d) ||
28077                 (!this.monthAllowBlank && !m) ||
28078                 (!this.yearAllowBlank && !y)
28079         ){
28080             valid = false;
28081         }
28082         
28083         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28084             return valid;
28085         }
28086         
28087         if(valid){
28088             this.markValid();
28089             return valid;
28090         }
28091         
28092         this.markInvalid();
28093         
28094         return valid;
28095     },
28096     
28097     markValid : function()
28098     {
28099         
28100         var label = this.el.select('label', true).first();
28101         var icon = this.el.select('i.fa-star', true).first();
28102
28103         if(label && icon){
28104             icon.remove();
28105         }
28106         
28107         this.fireEvent('valid', this);
28108     },
28109     
28110      /**
28111      * Mark this field as invalid
28112      * @param {String} msg The validation message
28113      */
28114     markInvalid : function(msg)
28115     {
28116         
28117         var label = this.el.select('label', true).first();
28118         var icon = this.el.select('i.fa-star', true).first();
28119
28120         if(label && !icon){
28121             this.el.select('.roo-date-split-field-label', true).createChild({
28122                 tag : 'i',
28123                 cls : 'text-danger fa fa-lg fa-star',
28124                 tooltip : 'This field is required',
28125                 style : 'margin-right:5px;'
28126             }, label, true);
28127         }
28128         
28129         this.fireEvent('invalid', this, msg);
28130     },
28131     
28132     clearInvalid : function()
28133     {
28134         var label = this.el.select('label', true).first();
28135         var icon = this.el.select('i.fa-star', true).first();
28136
28137         if(label && icon){
28138             icon.remove();
28139         }
28140         
28141         this.fireEvent('valid', this);
28142     },
28143     
28144     getName: function()
28145     {
28146         return this.name;
28147     }
28148     
28149 });
28150
28151