Roo/bootstrap/Component.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
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         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 Roo.log('start render...');
312                 Roo.log(cn);
313                 Roo.log(this[cntr](true));
314                 
315                 if(this[cntr](true) === false){
316                     Roo.log(['skip', cn]);
317                     return;
318                 }
319                 
320                 cn.render && cn.render(this[cntr](true));
321                 
322              }
323             // then add the element..
324         }
325         
326         
327         // handle the kids..
328         
329         var nitems = [];
330         /*
331         if (typeof (tree.menu) != 'undefined') {
332             tree.menu.parentType = cn.xtype;
333             tree.menu.triggerEl = cn.el;
334             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
335             
336         }
337         */
338         if (!tree.items || !tree.items.length) {
339             cn.items = nitems;
340             //Roo.log(["no children", this]);
341             
342             return cn;
343         }
344          
345         var items = tree.items;
346         delete tree.items;
347         
348         //Roo.log(items.length);
349             // add the items..
350         if (!skip_children) {    
351             for(var i =0;i < items.length;i++) {
352               //  Roo.log(['add child', items[i]]);
353                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
354             }
355         }
356         
357         cn.items = nitems;
358         
359         //Roo.log("fire childrenrendered");
360         
361         cn.fireEvent('childrenrendered', this);
362         
363         return cn;
364     },
365     /**
366      * Show a component - removes 'hidden' class
367      */
368     show : function()
369     {
370         if (this.el) {
371             this.el.removeClass('hidden');
372         }
373     },
374     /**
375      * Hide a component - adds 'hidden' class
376      */
377     hide: function()
378     {
379         if (this.el && !this.el.hasClass('hidden')) {
380             this.el.addClass('hidden');
381         }
382     }
383 });
384
385  /*
386  * - LGPL
387  *
388  * Body
389  *
390  */
391
392 /**
393  * @class Roo.bootstrap.Body
394  * @extends Roo.bootstrap.Component
395  * Bootstrap Body class
396  *
397  * @constructor
398  * Create a new body
399  * @param {Object} config The config object
400  */
401
402 Roo.bootstrap.Body = function(config){
403
404     config = config || {};
405
406     Roo.bootstrap.Body.superclass.constructor.call(this, config);
407     this.el = Roo.get(config.el ? config.el : document.body );
408     if (this.cls && this.cls.length) {
409         Roo.get(document.body).addClass(this.cls);
410     }
411 };
412
413 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
414
415     is_body : true,// just to make sure it's constructed?
416
417         autoCreate : {
418         cls: 'container'
419     },
420     onRender : function(ct, position)
421     {
422        /* Roo.log("Roo.bootstrap.Body - onRender");
423         if (this.cls && this.cls.length) {
424             Roo.get(document.body).addClass(this.cls);
425         }
426         // style??? xttr???
427         */
428     }
429
430
431
432
433 });
434 /*
435  * - LGPL
436  *
437  * button group
438  * 
439  */
440
441
442 /**
443  * @class Roo.bootstrap.ButtonGroup
444  * @extends Roo.bootstrap.Component
445  * Bootstrap ButtonGroup class
446  * @cfg {String} size lg | sm | xs (default empty normal)
447  * @cfg {String} align vertical | justified  (default none)
448  * @cfg {String} direction up | down (default down)
449  * @cfg {Boolean} toolbar false | true
450  * @cfg {Boolean} btn true | false
451  * 
452  * 
453  * @constructor
454  * Create a new Input
455  * @param {Object} config The config object
456  */
457
458 Roo.bootstrap.ButtonGroup = function(config){
459     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
460 };
461
462 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
463     
464     size: '',
465     align: '',
466     direction: '',
467     toolbar: false,
468     btn: true,
469
470     getAutoCreate : function(){
471         var cfg = {
472             cls: 'btn-group',
473             html : null
474         };
475         
476         cfg.html = this.html || cfg.html;
477         
478         if (this.toolbar) {
479             cfg = {
480                 cls: 'btn-toolbar',
481                 html: null
482             };
483             
484             return cfg;
485         }
486         
487         if (['vertical','justified'].indexOf(this.align)!==-1) {
488             cfg.cls = 'btn-group-' + this.align;
489             
490             if (this.align == 'justified') {
491                 console.log(this.items);
492             }
493         }
494         
495         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
496             cfg.cls += ' btn-group-' + this.size;
497         }
498         
499         if (this.direction == 'up') {
500             cfg.cls += ' dropup' ;
501         }
502         
503         return cfg;
504     }
505    
506 });
507
508  /*
509  * - LGPL
510  *
511  * button
512  * 
513  */
514
515 /**
516  * @class Roo.bootstrap.Button
517  * @extends Roo.bootstrap.Component
518  * Bootstrap Button class
519  * @cfg {String} html The button content
520  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
521  * @cfg {String} size ( lg | sm | xs)
522  * @cfg {String} tag ( a | input | submit)
523  * @cfg {String} href empty or href
524  * @cfg {Boolean} disabled default false;
525  * @cfg {Boolean} isClose default false;
526  * @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)
527  * @cfg {String} badge text for badge
528  * @cfg {String} theme default 
529  * @cfg {Boolean} inverse 
530  * @cfg {Boolean} toggle 
531  * @cfg {String} ontext text for on toggle state
532  * @cfg {String} offtext text for off toggle state
533  * @cfg {Boolean} defaulton 
534  * @cfg {Boolean} preventDefault  default true
535  * @cfg {Boolean} removeClass remove the standard class..
536  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
537  * 
538  * @constructor
539  * Create a new button
540  * @param {Object} config The config object
541  */
542
543
544 Roo.bootstrap.Button = function(config){
545     Roo.bootstrap.Button.superclass.constructor.call(this, config);
546     this.weightClass = ["btn-default", 
547                        "btn-primary", 
548                        "btn-success", 
549                        "btn-info", 
550                        "btn-warning",
551                        "btn-danger",
552                        "btn-link"
553                       ],  
554     this.addEvents({
555         // raw events
556         /**
557          * @event click
558          * When a butotn is pressed
559          * @param {Roo.bootstrap.Button} this
560          * @param {Roo.EventObject} e
561          */
562         "click" : true,
563          /**
564          * @event toggle
565          * After the button has been toggles
566          * @param {Roo.EventObject} e
567          * @param {boolean} pressed (also available as button.pressed)
568          */
569         "toggle" : true
570     });
571 };
572
573 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
574     html: false,
575     active: false,
576     weight: '',
577     size: '',
578     tag: 'button',
579     href: '',
580     disabled: false,
581     isClose: false,
582     glyphicon: '',
583     badge: '',
584     theme: 'default',
585     inverse: false,
586     
587     toggle: false,
588     ontext: 'ON',
589     offtext: 'OFF',
590     defaulton: true,
591     preventDefault: true,
592     removeClass: false,
593     name: false,
594     target: false,
595     
596     
597     pressed : null,
598      
599     
600     getAutoCreate : function(){
601         
602         var cfg = {
603             tag : 'button',
604             cls : 'roo-button',
605             html: ''
606         };
607         
608         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
609             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
610             this.tag = 'button';
611         } else {
612             cfg.tag = this.tag;
613         }
614         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
615         
616         if (this.toggle == true) {
617             cfg={
618                 tag: 'div',
619                 cls: 'slider-frame roo-button',
620                 cn: [
621                     {
622                         tag: 'span',
623                         'data-on-text':'ON',
624                         'data-off-text':'OFF',
625                         cls: 'slider-button',
626                         html: this.offtext
627                     }
628                 ]
629             };
630             
631             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
632                 cfg.cls += ' '+this.weight;
633             }
634             
635             return cfg;
636         }
637         
638         if (this.isClose) {
639             cfg.cls += ' close';
640             
641             cfg["aria-hidden"] = true;
642             
643             cfg.html = "&times;";
644             
645             return cfg;
646         }
647         
648          
649         if (this.theme==='default') {
650             cfg.cls = 'btn roo-button';
651             
652             //if (this.parentType != 'Navbar') {
653             this.weight = this.weight.length ?  this.weight : 'default';
654             //}
655             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
656                 
657                 cfg.cls += ' btn-' + this.weight;
658             }
659         } else if (this.theme==='glow') {
660             
661             cfg.tag = 'a';
662             cfg.cls = 'btn-glow roo-button';
663             
664             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
665                 
666                 cfg.cls += ' ' + this.weight;
667             }
668         }
669    
670         
671         if (this.inverse) {
672             this.cls += ' inverse';
673         }
674         
675         
676         if (this.active) {
677             cfg.cls += ' active';
678         }
679         
680         if (this.disabled) {
681             cfg.disabled = 'disabled';
682         }
683         
684         if (this.items) {
685             Roo.log('changing to ul' );
686             cfg.tag = 'ul';
687             this.glyphicon = 'caret';
688         }
689         
690         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
691          
692         //gsRoo.log(this.parentType);
693         if (this.parentType === 'Navbar' && !this.parent().bar) {
694             Roo.log('changing to li?');
695             
696             cfg.tag = 'li';
697             
698             cfg.cls = '';
699             cfg.cn =  [{
700                 tag : 'a',
701                 cls : 'roo-button',
702                 html : this.html,
703                 href : this.href || '#'
704             }];
705             if (this.menu) {
706                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
707                 cfg.cls += ' dropdown';
708             }   
709             
710             delete cfg.html;
711             
712         }
713         
714        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
715         
716         if (this.glyphicon) {
717             cfg.html = ' ' + cfg.html;
718             
719             cfg.cn = [
720                 {
721                     tag: 'span',
722                     cls: 'glyphicon glyphicon-' + this.glyphicon
723                 }
724             ];
725         }
726         
727         if (this.badge) {
728             cfg.html += ' ';
729             
730             cfg.tag = 'a';
731             
732 //            cfg.cls='btn roo-button';
733             
734             cfg.href=this.href;
735             
736             var value = cfg.html;
737             
738             if(this.glyphicon){
739                 value = {
740                             tag: 'span',
741                             cls: 'glyphicon glyphicon-' + this.glyphicon,
742                             html: this.html
743                         };
744                 
745             }
746             
747             cfg.cn = [
748                 value,
749                 {
750                     tag: 'span',
751                     cls: 'badge',
752                     html: this.badge
753                 }
754             ];
755             
756             cfg.html='';
757         }
758         
759         if (this.menu) {
760             cfg.cls += ' dropdown';
761             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
762         }
763         
764         if (cfg.tag !== 'a' && this.href !== '') {
765             throw "Tag must be a to set href.";
766         } else if (this.href.length > 0) {
767             cfg.href = this.href;
768         }
769         
770         if(this.removeClass){
771             cfg.cls = '';
772         }
773         
774         if(this.target){
775             cfg.target = this.target;
776         }
777         
778         return cfg;
779     },
780     initEvents: function() {
781        // Roo.log('init events?');
782 //        Roo.log(this.el.dom);
783         // add the menu...
784         
785         if (typeof (this.menu) != 'undefined') {
786             this.menu.parentType = this.xtype;
787             this.menu.triggerEl = this.el;
788             this.addxtype(Roo.apply({}, this.menu));
789         }
790
791
792        if (this.el.hasClass('roo-button')) {
793             this.el.on('click', this.onClick, this);
794        } else {
795             this.el.select('.roo-button').on('click', this.onClick, this);
796        }
797        
798        if(this.removeClass){
799            this.el.on('click', this.onClick, this);
800        }
801        
802        this.el.enableDisplayMode();
803         
804     },
805     onClick : function(e)
806     {
807         if (this.disabled) {
808             return;
809         }
810         
811         
812         Roo.log('button on click ');
813         if(this.preventDefault){
814             e.preventDefault();
815         }
816         if (this.pressed === true || this.pressed === false) {
817             this.pressed = !this.pressed;
818             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
819             this.fireEvent('toggle', this, e, this.pressed);
820         }
821         
822         
823         this.fireEvent('click', this, e);
824     },
825     
826     /**
827      * Enables this button
828      */
829     enable : function()
830     {
831         this.disabled = false;
832         this.el.removeClass('disabled');
833     },
834     
835     /**
836      * Disable this button
837      */
838     disable : function()
839     {
840         this.disabled = true;
841         this.el.addClass('disabled');
842     },
843      /**
844      * sets the active state on/off, 
845      * @param {Boolean} state (optional) Force a particular state
846      */
847     setActive : function(v) {
848         
849         this.el[v ? 'addClass' : 'removeClass']('active');
850     },
851      /**
852      * toggles the current active state 
853      */
854     toggleActive : function()
855     {
856        var active = this.el.hasClass('active');
857        this.setActive(!active);
858        
859         
860     },
861     setText : function(str)
862     {
863         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
864     },
865     getText : function()
866     {
867         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
868     },
869     hide: function() {
870        
871      
872         this.el.hide();   
873     },
874     show: function() {
875        
876         this.el.show();   
877     },
878     setWeight : function(str)
879     {
880           this.el.removeClass(this.weightClass);
881         this.el.addClass('btn-' + str);        
882     }
883     
884     
885 });
886
887  /*
888  * - LGPL
889  *
890  * column
891  * 
892  */
893
894 /**
895  * @class Roo.bootstrap.Column
896  * @extends Roo.bootstrap.Component
897  * Bootstrap Column class
898  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
899  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
900  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
901  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
902  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
903  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
904  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
905  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
906  *
907  * 
908  * @cfg {Boolean} hidden (true|false) hide the element
909  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
910  * @cfg {String} fa (ban|check|...) font awesome icon
911  * @cfg {Number} fasize (1|2|....) font awsome size
912
913  * @cfg {String} icon (info-sign|check|...) glyphicon name
914
915  * @cfg {String} html content of column.
916  * 
917  * @constructor
918  * Create a new Column
919  * @param {Object} config The config object
920  */
921
922 Roo.bootstrap.Column = function(config){
923     Roo.bootstrap.Column.superclass.constructor.call(this, config);
924 };
925
926 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
927     
928     xs: false,
929     sm: false,
930     md: false,
931     lg: false,
932     xsoff: false,
933     smoff: false,
934     mdoff: false,
935     lgoff: false,
936     html: '',
937     offset: 0,
938     alert: false,
939     fa: false,
940     icon : false,
941     hidden : false,
942     fasize : 1,
943     
944     getAutoCreate : function(){
945         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
946         
947         cfg = {
948             tag: 'div',
949             cls: 'column'
950         };
951         
952         var settings=this;
953         ['xs','sm','md','lg'].map(function(size){
954             //Roo.log( size + ':' + settings[size]);
955             
956             if (settings[size+'off'] !== false) {
957                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
958             }
959             
960             if (settings[size] === false) {
961                 return;
962             }
963             
964             if (!settings[size]) { // 0 = hidden
965                 cfg.cls += ' hidden-' + size;
966                 return;
967             }
968             cfg.cls += ' col-' + size + '-' + settings[size];
969             
970         });
971         
972         if (this.hidden) {
973             cfg.cls += ' hidden';
974         }
975         
976         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
977             cfg.cls +=' alert alert-' + this.alert;
978         }
979         
980         
981         if (this.html.length) {
982             cfg.html = this.html;
983         }
984         if (this.fa) {
985             var fasize = '';
986             if (this.fasize > 1) {
987                 fasize = ' fa-' + this.fasize + 'x';
988             }
989             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
990             
991             
992         }
993         if (this.icon) {
994             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
995         }
996         
997         return cfg;
998     }
999    
1000 });
1001
1002  
1003
1004  /*
1005  * - LGPL
1006  *
1007  * page container.
1008  * 
1009  */
1010
1011
1012 /**
1013  * @class Roo.bootstrap.Container
1014  * @extends Roo.bootstrap.Component
1015  * Bootstrap Container class
1016  * @cfg {Boolean} jumbotron is it a jumbotron element
1017  * @cfg {String} html content of element
1018  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1019  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1020  * @cfg {String} header content of header (for panel)
1021  * @cfg {String} footer content of footer (for panel)
1022  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1023  * @cfg {String} tag (header|aside|section) type of HTML tag.
1024  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1025  * @cfg {String} fa font awesome icon
1026  * @cfg {String} icon (info-sign|check|...) glyphicon name
1027  * @cfg {Boolean} hidden (true|false) hide the element
1028  * @cfg {Boolean} expandable (true|false) default false
1029  * @cfg {Boolean} expanded (true|false) default true
1030  * @cfg {String} rheader contet on the right of header
1031  * @cfg {Boolean} clickable (true|false) default false
1032
1033  *     
1034  * @constructor
1035  * Create a new Container
1036  * @param {Object} config The config object
1037  */
1038
1039 Roo.bootstrap.Container = function(config){
1040     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1041     
1042     this.addEvents({
1043         // raw events
1044          /**
1045          * @event expand
1046          * After the panel has been expand
1047          * 
1048          * @param {Roo.bootstrap.Container} this
1049          */
1050         "expand" : true,
1051         /**
1052          * @event collapse
1053          * After the panel has been collapsed
1054          * 
1055          * @param {Roo.bootstrap.Container} this
1056          */
1057         "collapse" : true,
1058         /**
1059          * @event click
1060          * When a element is chick
1061          * @param {Roo.bootstrap.Container} this
1062          * @param {Roo.EventObject} e
1063          */
1064         "click" : true
1065     });
1066 };
1067
1068 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1069     
1070     jumbotron : false,
1071     well: '',
1072     panel : '',
1073     header: '',
1074     footer : '',
1075     sticky: '',
1076     tag : false,
1077     alert : false,
1078     fa: false,
1079     icon : false,
1080     expandable : false,
1081     rheader : '',
1082     expanded : true,
1083     clickable: false,
1084   
1085      
1086     getChildContainer : function() {
1087         
1088         if(!this.el){
1089             return false;
1090         }
1091         
1092         if (this.panel.length) {
1093             return this.el.select('.panel-body',true).first();
1094         }
1095         
1096         return this.el;
1097     },
1098     
1099     
1100     getAutoCreate : function(){
1101         
1102         var cfg = {
1103             tag : this.tag || 'div',
1104             html : '',
1105             cls : ''
1106         };
1107         if (this.jumbotron) {
1108             cfg.cls = 'jumbotron';
1109         }
1110         
1111         
1112         
1113         // - this is applied by the parent..
1114         //if (this.cls) {
1115         //    cfg.cls = this.cls + '';
1116         //}
1117         
1118         if (this.sticky.length) {
1119             
1120             var bd = Roo.get(document.body);
1121             if (!bd.hasClass('bootstrap-sticky')) {
1122                 bd.addClass('bootstrap-sticky');
1123                 Roo.select('html',true).setStyle('height', '100%');
1124             }
1125              
1126             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1127         }
1128         
1129         
1130         if (this.well.length) {
1131             switch (this.well) {
1132                 case 'lg':
1133                 case 'sm':
1134                     cfg.cls +=' well well-' +this.well;
1135                     break;
1136                 default:
1137                     cfg.cls +=' well';
1138                     break;
1139             }
1140         }
1141         
1142         if (this.hidden) {
1143             cfg.cls += ' hidden';
1144         }
1145         
1146         
1147         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1148             cfg.cls +=' alert alert-' + this.alert;
1149         }
1150         
1151         var body = cfg;
1152         
1153         if (this.panel.length) {
1154             cfg.cls += ' panel panel-' + this.panel;
1155             cfg.cn = [];
1156             if (this.header.length) {
1157                 
1158                 var h = [];
1159                 
1160                 if(this.expandable){
1161                     
1162                     cfg.cls = cfg.cls + ' expandable';
1163                     
1164                     h.push({
1165                         tag: 'i',
1166                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1167                     });
1168                     
1169                 }
1170                 
1171                 h.push(
1172                     {
1173                         tag: 'span',
1174                         cls : 'panel-title',
1175                         html : (this.expandable ? '&nbsp;' : '') + this.header
1176                     },
1177                     {
1178                         tag: 'span',
1179                         cls: 'panel-header-right',
1180                         html: this.rheader
1181                     }
1182                 );
1183                 
1184                 cfg.cn.push({
1185                     cls : 'panel-heading',
1186                     style : this.expandable ? 'cursor: pointer' : '',
1187                     cn : h
1188                 });
1189                 
1190             }
1191             
1192             body = false;
1193             cfg.cn.push({
1194                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1195                 html : this.html
1196             });
1197             
1198             
1199             if (this.footer.length) {
1200                 cfg.cn.push({
1201                     cls : 'panel-footer',
1202                     html : this.footer
1203                     
1204                 });
1205             }
1206             
1207         }
1208         
1209         if (body) {
1210             body.html = this.html || cfg.html;
1211             // prefix with the icons..
1212             if (this.fa) {
1213                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1214             }
1215             if (this.icon) {
1216                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1217             }
1218             
1219             
1220         }
1221         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1222             cfg.cls =  'container';
1223         }
1224         
1225         return cfg;
1226     },
1227     
1228     initEvents: function() 
1229     {
1230         if(this.expandable){
1231             var headerEl = this.headerEl();
1232         
1233             if(headerEl){
1234                 headerEl.on('click', this.onToggleClick, this);
1235             }
1236         }
1237         
1238         if(this.clickable){
1239             this.el.on('click', this.onClick, this);
1240         }
1241         
1242     },
1243     
1244     onToggleClick : function()
1245     {
1246         var headerEl = this.headerEl();
1247         
1248         if(!headerEl){
1249             return;
1250         }
1251         
1252         if(this.expanded){
1253             this.collapse();
1254             return;
1255         }
1256         
1257         this.expand();
1258     },
1259     
1260     expand : function()
1261     {
1262         if(this.fireEvent('expand', this)) {
1263             
1264             this.expanded = true;
1265             
1266             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1267             
1268             this.el.select('.panel-body',true).first().removeClass('hide');
1269             
1270             var toggleEl = this.toggleEl();
1271
1272             if(!toggleEl){
1273                 return;
1274             }
1275
1276             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1277         }
1278         
1279     },
1280     
1281     collapse : function()
1282     {
1283         if(this.fireEvent('collapse', this)) {
1284             
1285             this.expanded = false;
1286             
1287             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1288             this.el.select('.panel-body',true).first().addClass('hide');
1289         
1290             var toggleEl = this.toggleEl();
1291
1292             if(!toggleEl){
1293                 return;
1294             }
1295
1296             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1297         }
1298     },
1299     
1300     toggleEl : function()
1301     {
1302         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1303             return;
1304         }
1305         
1306         return this.el.select('.panel-heading .fa',true).first();
1307     },
1308     
1309     headerEl : function()
1310     {
1311         if(!this.el || !this.panel.length || !this.header.length){
1312             return;
1313         }
1314         
1315         return this.el.select('.panel-heading',true).first()
1316     },
1317     
1318     bodyEl : function()
1319     {
1320         if(!this.el || !this.panel.length){
1321             return;
1322         }
1323         
1324         return this.el.select('.panel-body',true).first()
1325     },
1326     
1327     titleEl : function()
1328     {
1329         if(!this.el || !this.panel.length || !this.header.length){
1330             return;
1331         }
1332         
1333         return this.el.select('.panel-title',true).first();
1334     },
1335     
1336     setTitle : function(v)
1337     {
1338         var titleEl = this.titleEl();
1339         
1340         if(!titleEl){
1341             return;
1342         }
1343         
1344         titleEl.dom.innerHTML = v;
1345     },
1346     
1347     getTitle : function()
1348     {
1349         
1350         var titleEl = this.titleEl();
1351         
1352         if(!titleEl){
1353             return '';
1354         }
1355         
1356         return titleEl.dom.innerHTML;
1357     },
1358     
1359     setRightTitle : function(v)
1360     {
1361         var t = this.el.select('.panel-header-right',true).first();
1362         
1363         if(!t){
1364             return;
1365         }
1366         
1367         t.dom.innerHTML = v;
1368     },
1369     
1370     onClick : function(e)
1371     {
1372         e.preventDefault();
1373         
1374         this.fireEvent('click', this, e);
1375     }
1376    
1377 });
1378
1379  /*
1380  * - LGPL
1381  *
1382  * image
1383  * 
1384  */
1385
1386
1387 /**
1388  * @class Roo.bootstrap.Img
1389  * @extends Roo.bootstrap.Component
1390  * Bootstrap Img class
1391  * @cfg {Boolean} imgResponsive false | true
1392  * @cfg {String} border rounded | circle | thumbnail
1393  * @cfg {String} src image source
1394  * @cfg {String} alt image alternative text
1395  * @cfg {String} href a tag href
1396  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1397  * @cfg {String} xsUrl xs image source
1398  * @cfg {String} smUrl sm image source
1399  * @cfg {String} mdUrl md image source
1400  * @cfg {String} lgUrl lg image source
1401  * 
1402  * @constructor
1403  * Create a new Input
1404  * @param {Object} config The config object
1405  */
1406
1407 Roo.bootstrap.Img = function(config){
1408     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1409     
1410     this.addEvents({
1411         // img events
1412         /**
1413          * @event click
1414          * The img click event for the img.
1415          * @param {Roo.EventObject} e
1416          */
1417         "click" : true
1418     });
1419 };
1420
1421 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1422     
1423     imgResponsive: true,
1424     border: '',
1425     src: 'about:blank',
1426     href: false,
1427     target: false,
1428     xsUrl: '',
1429     smUrl: '',
1430     mdUrl: '',
1431     lgUrl: '',
1432
1433     getAutoCreate : function()
1434     {   
1435         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1436             return this.createSingleImg();
1437         }
1438         
1439         var cfg = {
1440             tag: 'div',
1441             cls: 'roo-image-responsive-group',
1442             cn: []
1443         };
1444         var _this = this;
1445         
1446         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1447             
1448             if(!_this[size + 'Url']){
1449                 return;
1450             }
1451             
1452             var img = {
1453                 tag: 'img',
1454                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1455                 html: _this.html || cfg.html,
1456                 src: _this[size + 'Url']
1457             };
1458             
1459             img.cls += ' roo-image-responsive-' + size;
1460             
1461             var s = ['xs', 'sm', 'md', 'lg'];
1462             
1463             s.splice(s.indexOf(size), 1);
1464             
1465             Roo.each(s, function(ss){
1466                 img.cls += ' hidden-' + ss;
1467             });
1468             
1469             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1470                 cfg.cls += ' img-' + _this.border;
1471             }
1472             
1473             if(_this.alt){
1474                 cfg.alt = _this.alt;
1475             }
1476             
1477             if(_this.href){
1478                 var a = {
1479                     tag: 'a',
1480                     href: _this.href,
1481                     cn: [
1482                         img
1483                     ]
1484                 };
1485
1486                 if(this.target){
1487                     a.target = _this.target;
1488                 }
1489             }
1490             
1491             cfg.cn.push((_this.href) ? a : img);
1492             
1493         });
1494         
1495         return cfg;
1496     },
1497     
1498     createSingleImg : function()
1499     {
1500         var cfg = {
1501             tag: 'img',
1502             cls: (this.imgResponsive) ? 'img-responsive' : '',
1503             html : null,
1504             src : 'about:blank'  // just incase src get's set to undefined?!?
1505         };
1506         
1507         cfg.html = this.html || cfg.html;
1508         
1509         cfg.src = this.src || cfg.src;
1510         
1511         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1512             cfg.cls += ' img-' + this.border;
1513         }
1514         
1515         if(this.alt){
1516             cfg.alt = this.alt;
1517         }
1518         
1519         if(this.href){
1520             var a = {
1521                 tag: 'a',
1522                 href: this.href,
1523                 cn: [
1524                     cfg
1525                 ]
1526             };
1527             
1528             if(this.target){
1529                 a.target = this.target;
1530             }
1531             
1532         }
1533         
1534         return (this.href) ? a : cfg;
1535     },
1536     
1537     initEvents: function() 
1538     {
1539         if(!this.href){
1540             this.el.on('click', this.onClick, this);
1541         }
1542         
1543     },
1544     
1545     onClick : function(e)
1546     {
1547         Roo.log('img onclick');
1548         this.fireEvent('click', this, e);
1549     },
1550     /**
1551      * Sets the url of the image - used to update it
1552      * @param {String} url the url of the image
1553      */
1554     
1555     setSrc : function(url)
1556     {
1557         this.src =  url;
1558         
1559         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1560             this.el.dom.src =  url;
1561             return;
1562         }
1563         
1564         this.el.select('img', true).first().dom.src =  url;
1565     }
1566     
1567     
1568    
1569 });
1570
1571  /*
1572  * - LGPL
1573  *
1574  * image
1575  * 
1576  */
1577
1578
1579 /**
1580  * @class Roo.bootstrap.Link
1581  * @extends Roo.bootstrap.Component
1582  * Bootstrap Link Class
1583  * @cfg {String} alt image alternative text
1584  * @cfg {String} href a tag href
1585  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1586  * @cfg {String} html the content of the link.
1587  * @cfg {String} anchor name for the anchor link
1588  * @cfg {String} fa - favicon
1589
1590  * @cfg {Boolean} preventDefault (true | false) default false
1591
1592  * 
1593  * @constructor
1594  * Create a new Input
1595  * @param {Object} config The config object
1596  */
1597
1598 Roo.bootstrap.Link = function(config){
1599     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1600     
1601     this.addEvents({
1602         // img events
1603         /**
1604          * @event click
1605          * The img click event for the img.
1606          * @param {Roo.EventObject} e
1607          */
1608         "click" : true
1609     });
1610 };
1611
1612 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1613     
1614     href: false,
1615     target: false,
1616     preventDefault: false,
1617     anchor : false,
1618     alt : false,
1619     fa: false,
1620
1621
1622     getAutoCreate : function()
1623     {
1624         var html = this.html || '';
1625         
1626         if (this.fa !== false) {
1627             html = '<i class="fa fa-' + this.fa + '"></i>';
1628         }
1629         var cfg = {
1630             tag: 'a'
1631         };
1632         // anchor's do not require html/href...
1633         if (this.anchor === false) {
1634             cfg.html = html;
1635             cfg.href = this.href || '#';
1636         } else {
1637             cfg.name = this.anchor;
1638             if (this.html !== false || this.fa !== false) {
1639                 cfg.html = html;
1640             }
1641             if (this.href !== false) {
1642                 cfg.href = this.href;
1643             }
1644         }
1645         
1646         if(this.alt !== false){
1647             cfg.alt = this.alt;
1648         }
1649         
1650         
1651         if(this.target !== false) {
1652             cfg.target = this.target;
1653         }
1654         
1655         return cfg;
1656     },
1657     
1658     initEvents: function() {
1659         
1660         if(!this.href || this.preventDefault){
1661             this.el.on('click', this.onClick, this);
1662         }
1663     },
1664     
1665     onClick : function(e)
1666     {
1667         if(this.preventDefault){
1668             e.preventDefault();
1669         }
1670         //Roo.log('img onclick');
1671         this.fireEvent('click', this, e);
1672     }
1673    
1674 });
1675
1676  /*
1677  * - LGPL
1678  *
1679  * header
1680  * 
1681  */
1682
1683 /**
1684  * @class Roo.bootstrap.Header
1685  * @extends Roo.bootstrap.Component
1686  * Bootstrap Header class
1687  * @cfg {String} html content of header
1688  * @cfg {Number} level (1|2|3|4|5|6) default 1
1689  * 
1690  * @constructor
1691  * Create a new Header
1692  * @param {Object} config The config object
1693  */
1694
1695
1696 Roo.bootstrap.Header  = function(config){
1697     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1698 };
1699
1700 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1701     
1702     //href : false,
1703     html : false,
1704     level : 1,
1705     
1706     
1707     
1708     getAutoCreate : function(){
1709         
1710         
1711         
1712         var cfg = {
1713             tag: 'h' + (1 *this.level),
1714             html: this.html || ''
1715         } ;
1716         
1717         return cfg;
1718     }
1719    
1720 });
1721
1722  
1723
1724  /*
1725  * Based on:
1726  * Ext JS Library 1.1.1
1727  * Copyright(c) 2006-2007, Ext JS, LLC.
1728  *
1729  * Originally Released Under LGPL - original licence link has changed is not relivant.
1730  *
1731  * Fork - LGPL
1732  * <script type="text/javascript">
1733  */
1734  
1735 /**
1736  * @class Roo.bootstrap.MenuMgr
1737  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1738  * @singleton
1739  */
1740 Roo.bootstrap.MenuMgr = function(){
1741    var menus, active, groups = {}, attached = false, lastShow = new Date();
1742
1743    // private - called when first menu is created
1744    function init(){
1745        menus = {};
1746        active = new Roo.util.MixedCollection();
1747        Roo.get(document).addKeyListener(27, function(){
1748            if(active.length > 0){
1749                hideAll();
1750            }
1751        });
1752    }
1753
1754    // private
1755    function hideAll(){
1756        if(active && active.length > 0){
1757            var c = active.clone();
1758            c.each(function(m){
1759                m.hide();
1760            });
1761        }
1762    }
1763
1764    // private
1765    function onHide(m){
1766        active.remove(m);
1767        if(active.length < 1){
1768            Roo.get(document).un("mouseup", onMouseDown);
1769             
1770            attached = false;
1771        }
1772    }
1773
1774    // private
1775    function onShow(m){
1776        var last = active.last();
1777        lastShow = new Date();
1778        active.add(m);
1779        if(!attached){
1780           Roo.get(document).on("mouseup", onMouseDown);
1781            
1782            attached = true;
1783        }
1784        if(m.parentMenu){
1785           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1786           m.parentMenu.activeChild = m;
1787        }else if(last && last.isVisible()){
1788           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1789        }
1790    }
1791
1792    // private
1793    function onBeforeHide(m){
1794        if(m.activeChild){
1795            m.activeChild.hide();
1796        }
1797        if(m.autoHideTimer){
1798            clearTimeout(m.autoHideTimer);
1799            delete m.autoHideTimer;
1800        }
1801    }
1802
1803    // private
1804    function onBeforeShow(m){
1805        var pm = m.parentMenu;
1806        if(!pm && !m.allowOtherMenus){
1807            hideAll();
1808        }else if(pm && pm.activeChild && active != m){
1809            pm.activeChild.hide();
1810        }
1811    }
1812
1813    // private this should really trigger on mouseup..
1814    function onMouseDown(e){
1815         Roo.log("on Mouse Up");
1816         
1817         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1818             Roo.log("MenuManager hideAll");
1819             hideAll();
1820             e.stopEvent();
1821         }
1822         
1823         
1824    }
1825
1826    // private
1827    function onBeforeCheck(mi, state){
1828        if(state){
1829            var g = groups[mi.group];
1830            for(var i = 0, l = g.length; i < l; i++){
1831                if(g[i] != mi){
1832                    g[i].setChecked(false);
1833                }
1834            }
1835        }
1836    }
1837
1838    return {
1839
1840        /**
1841         * Hides all menus that are currently visible
1842         */
1843        hideAll : function(){
1844             hideAll();  
1845        },
1846
1847        // private
1848        register : function(menu){
1849            if(!menus){
1850                init();
1851            }
1852            menus[menu.id] = menu;
1853            menu.on("beforehide", onBeforeHide);
1854            menu.on("hide", onHide);
1855            menu.on("beforeshow", onBeforeShow);
1856            menu.on("show", onShow);
1857            var g = menu.group;
1858            if(g && menu.events["checkchange"]){
1859                if(!groups[g]){
1860                    groups[g] = [];
1861                }
1862                groups[g].push(menu);
1863                menu.on("checkchange", onCheck);
1864            }
1865        },
1866
1867         /**
1868          * Returns a {@link Roo.menu.Menu} object
1869          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1870          * be used to generate and return a new Menu instance.
1871          */
1872        get : function(menu){
1873            if(typeof menu == "string"){ // menu id
1874                return menus[menu];
1875            }else if(menu.events){  // menu instance
1876                return menu;
1877            }
1878            /*else if(typeof menu.length == 'number'){ // array of menu items?
1879                return new Roo.bootstrap.Menu({items:menu});
1880            }else{ // otherwise, must be a config
1881                return new Roo.bootstrap.Menu(menu);
1882            }
1883            */
1884            return false;
1885        },
1886
1887        // private
1888        unregister : function(menu){
1889            delete menus[menu.id];
1890            menu.un("beforehide", onBeforeHide);
1891            menu.un("hide", onHide);
1892            menu.un("beforeshow", onBeforeShow);
1893            menu.un("show", onShow);
1894            var g = menu.group;
1895            if(g && menu.events["checkchange"]){
1896                groups[g].remove(menu);
1897                menu.un("checkchange", onCheck);
1898            }
1899        },
1900
1901        // private
1902        registerCheckable : function(menuItem){
1903            var g = menuItem.group;
1904            if(g){
1905                if(!groups[g]){
1906                    groups[g] = [];
1907                }
1908                groups[g].push(menuItem);
1909                menuItem.on("beforecheckchange", onBeforeCheck);
1910            }
1911        },
1912
1913        // private
1914        unregisterCheckable : function(menuItem){
1915            var g = menuItem.group;
1916            if(g){
1917                groups[g].remove(menuItem);
1918                menuItem.un("beforecheckchange", onBeforeCheck);
1919            }
1920        }
1921    };
1922 }();/*
1923  * - LGPL
1924  *
1925  * menu
1926  * 
1927  */
1928
1929 /**
1930  * @class Roo.bootstrap.Menu
1931  * @extends Roo.bootstrap.Component
1932  * Bootstrap Menu class - container for MenuItems
1933  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1934  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1935  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1936  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1937  * 
1938  * @constructor
1939  * Create a new Menu
1940  * @param {Object} config The config object
1941  */
1942
1943
1944 Roo.bootstrap.Menu = function(config){
1945     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1946     if (this.registerMenu && this.type != 'treeview')  {
1947         Roo.bootstrap.MenuMgr.register(this);
1948     }
1949     this.addEvents({
1950         /**
1951          * @event beforeshow
1952          * Fires before this menu is displayed
1953          * @param {Roo.menu.Menu} this
1954          */
1955         beforeshow : true,
1956         /**
1957          * @event beforehide
1958          * Fires before this menu is hidden
1959          * @param {Roo.menu.Menu} this
1960          */
1961         beforehide : true,
1962         /**
1963          * @event show
1964          * Fires after this menu is displayed
1965          * @param {Roo.menu.Menu} this
1966          */
1967         show : true,
1968         /**
1969          * @event hide
1970          * Fires after this menu is hidden
1971          * @param {Roo.menu.Menu} this
1972          */
1973         hide : true,
1974         /**
1975          * @event click
1976          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1977          * @param {Roo.menu.Menu} this
1978          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1979          * @param {Roo.EventObject} e
1980          */
1981         click : true,
1982         /**
1983          * @event mouseover
1984          * Fires when the mouse is hovering over this menu
1985          * @param {Roo.menu.Menu} this
1986          * @param {Roo.EventObject} e
1987          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1988          */
1989         mouseover : true,
1990         /**
1991          * @event mouseout
1992          * Fires when the mouse exits this menu
1993          * @param {Roo.menu.Menu} this
1994          * @param {Roo.EventObject} e
1995          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1996          */
1997         mouseout : true,
1998         /**
1999          * @event itemclick
2000          * Fires when a menu item contained in this menu is clicked
2001          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2002          * @param {Roo.EventObject} e
2003          */
2004         itemclick: true
2005     });
2006     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2007 };
2008
2009 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2010     
2011    /// html : false,
2012     //align : '',
2013     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2014     type: false,
2015     /**
2016      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2017      */
2018     registerMenu : true,
2019     
2020     menuItems :false, // stores the menu items..
2021     
2022     hidden:true,
2023         
2024     parentMenu : false,
2025     
2026     stopEvent : true,
2027     
2028     isLink : false,
2029     
2030     getChildContainer : function() {
2031         return this.el;  
2032     },
2033     
2034     getAutoCreate : function(){
2035          
2036         //if (['right'].indexOf(this.align)!==-1) {
2037         //    cfg.cn[1].cls += ' pull-right'
2038         //}
2039         
2040         
2041         var cfg = {
2042             tag : 'ul',
2043             cls : 'dropdown-menu' ,
2044             style : 'z-index:1000'
2045             
2046         };
2047         
2048         if (this.type === 'submenu') {
2049             cfg.cls = 'submenu active';
2050         }
2051         if (this.type === 'treeview') {
2052             cfg.cls = 'treeview-menu';
2053         }
2054         
2055         return cfg;
2056     },
2057     initEvents : function() {
2058         
2059        // Roo.log("ADD event");
2060        // Roo.log(this.triggerEl.dom);
2061         
2062         this.triggerEl.on('click', this.onTriggerClick, this);
2063         
2064         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2065         
2066         this.triggerEl.addClass('dropdown-toggle');
2067         
2068         if (Roo.isTouch) {
2069             this.el.on('touchstart'  , this.onTouch, this);
2070         }
2071         this.el.on('click' , this.onClick, this);
2072
2073         this.el.on("mouseover", this.onMouseOver, this);
2074         this.el.on("mouseout", this.onMouseOut, this);
2075         
2076     },
2077     
2078     findTargetItem : function(e)
2079     {
2080         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2081         if(!t){
2082             return false;
2083         }
2084         //Roo.log(t);         Roo.log(t.id);
2085         if(t && t.id){
2086             //Roo.log(this.menuitems);
2087             return this.menuitems.get(t.id);
2088             
2089             //return this.items.get(t.menuItemId);
2090         }
2091         
2092         return false;
2093     },
2094     
2095     onTouch : function(e) 
2096     {
2097         Roo.log("menu.onTouch");
2098         //e.stopEvent(); this make the user popdown broken
2099         this.onClick(e);
2100     },
2101     
2102     onClick : function(e)
2103     {
2104         Roo.log("menu.onClick");
2105         
2106         var t = this.findTargetItem(e);
2107         if(!t || t.isContainer){
2108             return;
2109         }
2110         Roo.log(e);
2111         /*
2112         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2113             if(t == this.activeItem && t.shouldDeactivate(e)){
2114                 this.activeItem.deactivate();
2115                 delete this.activeItem;
2116                 return;
2117             }
2118             if(t.canActivate){
2119                 this.setActiveItem(t, true);
2120             }
2121             return;
2122             
2123             
2124         }
2125         */
2126        
2127         Roo.log('pass click event');
2128         
2129         t.onClick(e);
2130         
2131         this.fireEvent("click", this, t, e);
2132         
2133         var _this = this;
2134         
2135         if(!t.href.length || t.href == '#'){
2136             (function() { _this.hide(); }).defer(100);
2137         }
2138         
2139     },
2140     
2141     onMouseOver : function(e){
2142         var t  = this.findTargetItem(e);
2143         //Roo.log(t);
2144         //if(t){
2145         //    if(t.canActivate && !t.disabled){
2146         //        this.setActiveItem(t, true);
2147         //    }
2148         //}
2149         
2150         this.fireEvent("mouseover", this, e, t);
2151     },
2152     isVisible : function(){
2153         return !this.hidden;
2154     },
2155      onMouseOut : function(e){
2156         var t  = this.findTargetItem(e);
2157         
2158         //if(t ){
2159         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2160         //        this.activeItem.deactivate();
2161         //        delete this.activeItem;
2162         //    }
2163         //}
2164         this.fireEvent("mouseout", this, e, t);
2165     },
2166     
2167     
2168     /**
2169      * Displays this menu relative to another element
2170      * @param {String/HTMLElement/Roo.Element} element The element to align to
2171      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2172      * the element (defaults to this.defaultAlign)
2173      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2174      */
2175     show : function(el, pos, parentMenu){
2176         this.parentMenu = parentMenu;
2177         if(!this.el){
2178             this.render();
2179         }
2180         this.fireEvent("beforeshow", this);
2181         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2182     },
2183      /**
2184      * Displays this menu at a specific xy position
2185      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2186      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2187      */
2188     showAt : function(xy, parentMenu, /* private: */_e){
2189         this.parentMenu = parentMenu;
2190         if(!this.el){
2191             this.render();
2192         }
2193         if(_e !== false){
2194             this.fireEvent("beforeshow", this);
2195             //xy = this.el.adjustForConstraints(xy);
2196         }
2197         
2198         //this.el.show();
2199         this.hideMenuItems();
2200         this.hidden = false;
2201         this.triggerEl.addClass('open');
2202         
2203         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2204             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2205         }
2206         
2207         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2208             this.el.setXY(xy);
2209         }
2210         
2211         this.focus();
2212         this.fireEvent("show", this);
2213     },
2214     
2215     focus : function(){
2216         return;
2217         if(!this.hidden){
2218             this.doFocus.defer(50, this);
2219         }
2220     },
2221
2222     doFocus : function(){
2223         if(!this.hidden){
2224             this.focusEl.focus();
2225         }
2226     },
2227
2228     /**
2229      * Hides this menu and optionally all parent menus
2230      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2231      */
2232     hide : function(deep)
2233     {
2234         
2235         this.hideMenuItems();
2236         if(this.el && this.isVisible()){
2237             this.fireEvent("beforehide", this);
2238             if(this.activeItem){
2239                 this.activeItem.deactivate();
2240                 this.activeItem = null;
2241             }
2242             this.triggerEl.removeClass('open');;
2243             this.hidden = true;
2244             this.fireEvent("hide", this);
2245         }
2246         if(deep === true && this.parentMenu){
2247             this.parentMenu.hide(true);
2248         }
2249     },
2250     
2251     onTriggerClick : function(e)
2252     {
2253         Roo.log('trigger click');
2254         
2255         var target = e.getTarget();
2256         
2257         Roo.log(target.nodeName.toLowerCase());
2258         
2259         if(target.nodeName.toLowerCase() === 'i'){
2260             e.preventDefault();
2261         }
2262         
2263     },
2264     
2265     onTriggerPress  : function(e)
2266     {
2267         Roo.log('trigger press');
2268         //Roo.log(e.getTarget());
2269        // Roo.log(this.triggerEl.dom);
2270        
2271         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2272         var pel = Roo.get(e.getTarget());
2273         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2274             Roo.log('is treeview or dropdown?');
2275             return;
2276         }
2277         
2278         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2279             return;
2280         }
2281         
2282         if (this.isVisible()) {
2283             Roo.log('hide');
2284             this.hide();
2285         } else {
2286             Roo.log('show');
2287             this.show(this.triggerEl, false, false);
2288         }
2289         
2290         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2291             e.stopEvent();
2292         }
2293         
2294     },
2295        
2296     
2297     hideMenuItems : function()
2298     {
2299         Roo.log("hide Menu Items");
2300         if (!this.el) { 
2301             return;
2302         }
2303         //$(backdrop).remove()
2304         this.el.select('.open',true).each(function(aa) {
2305             
2306             aa.removeClass('open');
2307           //var parent = getParent($(this))
2308           //var relatedTarget = { relatedTarget: this }
2309           
2310            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2311           //if (e.isDefaultPrevented()) return
2312            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2313         });
2314     },
2315     addxtypeChild : function (tree, cntr) {
2316         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2317           
2318         this.menuitems.add(comp);
2319         return comp;
2320
2321     },
2322     getEl : function()
2323     {
2324         Roo.log(this.el);
2325         return this.el;
2326     }
2327 });
2328
2329  
2330  /*
2331  * - LGPL
2332  *
2333  * menu item
2334  * 
2335  */
2336
2337
2338 /**
2339  * @class Roo.bootstrap.MenuItem
2340  * @extends Roo.bootstrap.Component
2341  * Bootstrap MenuItem class
2342  * @cfg {String} html the menu label
2343  * @cfg {String} href the link
2344  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2345  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2346  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2347  * @cfg {String} fa favicon to show on left of menu item.
2348  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2349  * 
2350  * 
2351  * @constructor
2352  * Create a new MenuItem
2353  * @param {Object} config The config object
2354  */
2355
2356
2357 Roo.bootstrap.MenuItem = function(config){
2358     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2359     this.addEvents({
2360         // raw events
2361         /**
2362          * @event click
2363          * The raw click event for the entire grid.
2364          * @param {Roo.bootstrap.MenuItem} this
2365          * @param {Roo.EventObject} e
2366          */
2367         "click" : true
2368     });
2369 };
2370
2371 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2372     
2373     href : false,
2374     html : false,
2375     preventDefault: false,
2376     isContainer : false,
2377     active : false,
2378     fa: false,
2379     
2380     getAutoCreate : function(){
2381         
2382         if(this.isContainer){
2383             return {
2384                 tag: 'li',
2385                 cls: 'dropdown-menu-item'
2386             };
2387         }
2388         var ctag = {
2389             tag: 'span',
2390             html: 'Link'
2391         };
2392         
2393         var anc = {
2394             tag : 'a',
2395             href : '#',
2396             cn : [  ]
2397         };
2398         
2399         if (this.fa !== false) {
2400             anc.cn.push({
2401                 tag : 'i',
2402                 cls : 'fa fa-' + this.fa
2403             });
2404         }
2405         
2406         anc.cn.push(ctag);
2407         
2408         
2409         var cfg= {
2410             tag: 'li',
2411             cls: 'dropdown-menu-item',
2412             cn: [ anc ]
2413         };
2414         if (this.parent().type == 'treeview') {
2415             cfg.cls = 'treeview-menu';
2416         }
2417         if (this.active) {
2418             cfg.cls += ' active';
2419         }
2420         
2421         
2422         
2423         anc.href = this.href || cfg.cn[0].href ;
2424         ctag.html = this.html || cfg.cn[0].html ;
2425         return cfg;
2426     },
2427     
2428     initEvents: function()
2429     {
2430         if (this.parent().type == 'treeview') {
2431             this.el.select('a').on('click', this.onClick, this);
2432         }
2433         
2434         if (this.menu) {
2435             this.menu.parentType = this.xtype;
2436             this.menu.triggerEl = this.el;
2437             this.menu = this.addxtype(Roo.apply({}, this.menu));
2438         }
2439         
2440     },
2441     onClick : function(e)
2442     {
2443         Roo.log('item on click ');
2444         
2445         if(this.preventDefault){
2446             e.preventDefault();
2447         }
2448         //this.parent().hideMenuItems();
2449         
2450         this.fireEvent('click', this, e);
2451     },
2452     getEl : function()
2453     {
2454         return this.el;
2455     } 
2456 });
2457
2458  
2459
2460  /*
2461  * - LGPL
2462  *
2463  * menu separator
2464  * 
2465  */
2466
2467
2468 /**
2469  * @class Roo.bootstrap.MenuSeparator
2470  * @extends Roo.bootstrap.Component
2471  * Bootstrap MenuSeparator class
2472  * 
2473  * @constructor
2474  * Create a new MenuItem
2475  * @param {Object} config The config object
2476  */
2477
2478
2479 Roo.bootstrap.MenuSeparator = function(config){
2480     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2481 };
2482
2483 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2484     
2485     getAutoCreate : function(){
2486         var cfg = {
2487             cls: 'divider',
2488             tag : 'li'
2489         };
2490         
2491         return cfg;
2492     }
2493    
2494 });
2495
2496  
2497
2498  
2499 /*
2500 * Licence: LGPL
2501 */
2502
2503 /**
2504  * @class Roo.bootstrap.Modal
2505  * @extends Roo.bootstrap.Component
2506  * Bootstrap Modal class
2507  * @cfg {String} title Title of dialog
2508  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2509  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2510  * @cfg {Boolean} specificTitle default false
2511  * @cfg {Array} buttons Array of buttons or standard button set..
2512  * @cfg {String} buttonPosition (left|right|center) default right
2513  * @cfg {Boolean} animate default true
2514  * @cfg {Boolean} allow_close default true
2515  * @cfg {Boolean} fitwindow default false
2516  * @cfg {String} size (sm|lg) default empty
2517  *
2518  *
2519  * @constructor
2520  * Create a new Modal Dialog
2521  * @param {Object} config The config object
2522  */
2523
2524 Roo.bootstrap.Modal = function(config){
2525     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2526     this.addEvents({
2527         // raw events
2528         /**
2529          * @event btnclick
2530          * The raw btnclick event for the button
2531          * @param {Roo.EventObject} e
2532          */
2533         "btnclick" : true,
2534         /**
2535          * @event resize
2536          * Fire when dialog resize
2537          * @param {Roo.bootstrap.Modal} this
2538          * @param {Roo.EventObject} e
2539          */
2540         "resize" : true
2541     });
2542     this.buttons = this.buttons || [];
2543
2544     if (this.tmpl) {
2545         this.tmpl = Roo.factory(this.tmpl);
2546     }
2547
2548 };
2549
2550 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2551
2552     title : 'test dialog',
2553
2554     buttons : false,
2555
2556     // set on load...
2557
2558     html: false,
2559
2560     tmp: false,
2561
2562     specificTitle: false,
2563
2564     buttonPosition: 'right',
2565
2566     allow_close : true,
2567
2568     animate : true,
2569
2570     fitwindow: false,
2571
2572
2573      // private
2574     dialogEl: false,
2575     bodyEl:  false,
2576     footerEl:  false,
2577     titleEl:  false,
2578     closeEl:  false,
2579
2580     size: '',
2581
2582
2583     onRender : function(ct, position)
2584     {
2585         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2586
2587         if(!this.el){
2588             var cfg = Roo.apply({},  this.getAutoCreate());
2589             cfg.id = Roo.id();
2590             //if(!cfg.name){
2591             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2592             //}
2593             //if (!cfg.name.length) {
2594             //    delete cfg.name;
2595            // }
2596             if (this.cls) {
2597                 cfg.cls += ' ' + this.cls;
2598             }
2599             if (this.style) {
2600                 cfg.style = this.style;
2601             }
2602             this.el = Roo.get(document.body).createChild(cfg, position);
2603         }
2604         //var type = this.el.dom.type;
2605
2606
2607         if(this.tabIndex !== undefined){
2608             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2609         }
2610
2611         this.dialogEl = this.el.select('.modal-dialog',true).first();
2612         this.bodyEl = this.el.select('.modal-body',true).first();
2613         this.closeEl = this.el.select('.modal-header .close', true).first();
2614         this.headerEl = this.el.select('.modal-header',true).first();
2615         this.titleEl = this.el.select('.modal-title',true).first();
2616         this.footerEl = this.el.select('.modal-footer',true).first();
2617
2618         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2619         this.maskEl.enableDisplayMode("block");
2620         this.maskEl.hide();
2621         //this.el.addClass("x-dlg-modal");
2622
2623         if (this.buttons.length) {
2624             Roo.each(this.buttons, function(bb) {
2625                 var b = Roo.apply({}, bb);
2626                 b.xns = b.xns || Roo.bootstrap;
2627                 b.xtype = b.xtype || 'Button';
2628                 if (typeof(b.listeners) == 'undefined') {
2629                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2630                 }
2631
2632                 var btn = Roo.factory(b);
2633
2634                 btn.render(this.el.select('.modal-footer div').first());
2635
2636             },this);
2637         }
2638         // render the children.
2639         var nitems = [];
2640
2641         if(typeof(this.items) != 'undefined'){
2642             var items = this.items;
2643             delete this.items;
2644
2645             for(var i =0;i < items.length;i++) {
2646                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2647             }
2648         }
2649
2650         this.items = nitems;
2651
2652         // where are these used - they used to be body/close/footer
2653
2654
2655         this.initEvents();
2656         //this.el.addClass([this.fieldClass, this.cls]);
2657
2658     },
2659
2660     getAutoCreate : function(){
2661
2662
2663         var bdy = {
2664                 cls : 'modal-body',
2665                 html : this.html || ''
2666         };
2667
2668         var title = {
2669             tag: 'h4',
2670             cls : 'modal-title',
2671             html : this.title
2672         };
2673
2674         if(this.specificTitle){
2675             title = this.title;
2676
2677         };
2678
2679         var header = [];
2680         if (this.allow_close) {
2681             header.push({
2682                 tag: 'button',
2683                 cls : 'close',
2684                 html : '&times'
2685             });
2686         }
2687
2688         header.push(title);
2689
2690         var size = '';
2691
2692         if(this.size.length){
2693             size = 'modal-' + this.size;
2694         }
2695
2696         var modal = {
2697             cls: "modal",
2698             style : 'display: none',
2699             cn : [
2700                 {
2701                     cls: "modal-dialog " + size,
2702                     cn : [
2703                         {
2704                             cls : "modal-content",
2705                             cn : [
2706                                 {
2707                                     cls : 'modal-header',
2708                                     cn : header
2709                                 },
2710                                 bdy,
2711                                 {
2712                                     cls : 'modal-footer',
2713                                     cn : [
2714                                         {
2715                                             tag: 'div',
2716                                             cls: 'btn-' + this.buttonPosition
2717                                         }
2718                                     ]
2719
2720                                 }
2721
2722
2723                             ]
2724
2725                         }
2726                     ]
2727
2728                 }
2729             ]
2730         };
2731
2732         if(this.animate){
2733             modal.cls += ' fade';
2734         }
2735
2736         return modal;
2737
2738     },
2739     getChildContainer : function() {
2740
2741          return this.bodyEl;
2742
2743     },
2744     getButtonContainer : function() {
2745          return this.el.select('.modal-footer div',true).first();
2746
2747     },
2748     initEvents : function()
2749     {
2750         if (this.allow_close) {
2751             this.closeEl.on('click', this.hide, this);
2752         }
2753         Roo.EventManager.onWindowResize(this.resize, this, true);
2754
2755
2756     },
2757
2758     resize : function()
2759     {
2760         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2761         if (this.fitwindow) {
2762             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2763             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2764             this.setSize(w,h);
2765         }
2766     },
2767
2768     setSize : function(w,h)
2769     {
2770         if (!w && !h) {
2771             return;
2772         }
2773         this.resizeTo(w,h);
2774     },
2775
2776     show : function() {
2777
2778         if (!this.rendered) {
2779             this.render();
2780         }
2781
2782         this.el.setStyle('display', 'block');
2783
2784         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2785             var _this = this;
2786             (function(){
2787                 this.el.addClass('in');
2788             }).defer(50, this);
2789         }else{
2790             this.el.addClass('in');
2791
2792         }
2793
2794         // not sure how we can show data in here..
2795         //if (this.tmpl) {
2796         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2797         //}
2798
2799         Roo.get(document.body).addClass("x-body-masked");
2800         
2801         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2802         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2803         this.maskEl.show();
2804         
2805         this.resize();
2806         
2807         this.fireEvent('show', this);
2808
2809         // set zindex here - otherwise it appears to be ignored...
2810         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2811
2812         (function () {
2813             this.items.forEach( function(e) {
2814                 e.layout ? e.layout() : false;
2815
2816             });
2817         }).defer(100,this);
2818
2819     },
2820     hide : function()
2821     {
2822         if(this.fireEvent("beforehide", this) !== false){
2823             this.maskEl.hide();
2824             Roo.get(document.body).removeClass("x-body-masked");
2825             this.el.removeClass('in');
2826             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2827
2828             if(this.animate){ // why
2829                 var _this = this;
2830                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2831             }else{
2832                 this.el.setStyle('display', 'none');
2833             }
2834             this.fireEvent('hide', this);
2835         }
2836     },
2837
2838     addButton : function(str, cb)
2839     {
2840
2841
2842         var b = Roo.apply({}, { html : str } );
2843         b.xns = b.xns || Roo.bootstrap;
2844         b.xtype = b.xtype || 'Button';
2845         if (typeof(b.listeners) == 'undefined') {
2846             b.listeners = { click : cb.createDelegate(this)  };
2847         }
2848
2849         var btn = Roo.factory(b);
2850
2851         btn.render(this.el.select('.modal-footer div').first());
2852
2853         return btn;
2854
2855     },
2856
2857     setDefaultButton : function(btn)
2858     {
2859         //this.el.select('.modal-footer').()
2860     },
2861     diff : false,
2862
2863     resizeTo: function(w,h)
2864     {
2865         // skip.. ?? why??
2866
2867         this.dialogEl.setWidth(w);
2868         if (this.diff === false) {
2869             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2870         }
2871
2872         this.bodyEl.setHeight(h-this.diff);
2873
2874         this.fireEvent('resize', this);
2875
2876     },
2877     setContentSize  : function(w, h)
2878     {
2879
2880     },
2881     onButtonClick: function(btn,e)
2882     {
2883         //Roo.log([a,b,c]);
2884         this.fireEvent('btnclick', btn.name, e);
2885     },
2886      /**
2887      * Set the title of the Dialog
2888      * @param {String} str new Title
2889      */
2890     setTitle: function(str) {
2891         this.titleEl.dom.innerHTML = str;
2892     },
2893     /**
2894      * Set the body of the Dialog
2895      * @param {String} str new Title
2896      */
2897     setBody: function(str) {
2898         this.bodyEl.dom.innerHTML = str;
2899     },
2900     /**
2901      * Set the body of the Dialog using the template
2902      * @param {Obj} data - apply this data to the template and replace the body contents.
2903      */
2904     applyBody: function(obj)
2905     {
2906         if (!this.tmpl) {
2907             Roo.log("Error - using apply Body without a template");
2908             //code
2909         }
2910         this.tmpl.overwrite(this.bodyEl, obj);
2911     }
2912
2913 });
2914
2915
2916 Roo.apply(Roo.bootstrap.Modal,  {
2917     /**
2918          * Button config that displays a single OK button
2919          * @type Object
2920          */
2921         OK :  [{
2922             name : 'ok',
2923             weight : 'primary',
2924             html : 'OK'
2925         }],
2926         /**
2927          * Button config that displays Yes and No buttons
2928          * @type Object
2929          */
2930         YESNO : [
2931             {
2932                 name  : 'no',
2933                 html : 'No'
2934             },
2935             {
2936                 name  :'yes',
2937                 weight : 'primary',
2938                 html : 'Yes'
2939             }
2940         ],
2941
2942         /**
2943          * Button config that displays OK and Cancel buttons
2944          * @type Object
2945          */
2946         OKCANCEL : [
2947             {
2948                name : 'cancel',
2949                 html : 'Cancel'
2950             },
2951             {
2952                 name : 'ok',
2953                 weight : 'primary',
2954                 html : 'OK'
2955             }
2956         ],
2957         /**
2958          * Button config that displays Yes, No and Cancel buttons
2959          * @type Object
2960          */
2961         YESNOCANCEL : [
2962             {
2963                 name : 'yes',
2964                 weight : 'primary',
2965                 html : 'Yes'
2966             },
2967             {
2968                 name : 'no',
2969                 html : 'No'
2970             },
2971             {
2972                 name : 'cancel',
2973                 html : 'Cancel'
2974             }
2975         ],
2976         
2977         zIndex : 10001
2978 });
2979 /*
2980  * - LGPL
2981  *
2982  * messagebox - can be used as a replace
2983  * 
2984  */
2985 /**
2986  * @class Roo.MessageBox
2987  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2988  * Example usage:
2989  *<pre><code>
2990 // Basic alert:
2991 Roo.Msg.alert('Status', 'Changes saved successfully.');
2992
2993 // Prompt for user data:
2994 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2995     if (btn == 'ok'){
2996         // process text value...
2997     }
2998 });
2999
3000 // Show a dialog using config options:
3001 Roo.Msg.show({
3002    title:'Save Changes?',
3003    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3004    buttons: Roo.Msg.YESNOCANCEL,
3005    fn: processResult,
3006    animEl: 'elId'
3007 });
3008 </code></pre>
3009  * @singleton
3010  */
3011 Roo.bootstrap.MessageBox = function(){
3012     var dlg, opt, mask, waitTimer;
3013     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3014     var buttons, activeTextEl, bwidth;
3015
3016     
3017     // private
3018     var handleButton = function(button){
3019         dlg.hide();
3020         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3021     };
3022
3023     // private
3024     var handleHide = function(){
3025         if(opt && opt.cls){
3026             dlg.el.removeClass(opt.cls);
3027         }
3028         //if(waitTimer){
3029         //    Roo.TaskMgr.stop(waitTimer);
3030         //    waitTimer = null;
3031         //}
3032     };
3033
3034     // private
3035     var updateButtons = function(b){
3036         var width = 0;
3037         if(!b){
3038             buttons["ok"].hide();
3039             buttons["cancel"].hide();
3040             buttons["yes"].hide();
3041             buttons["no"].hide();
3042             //dlg.footer.dom.style.display = 'none';
3043             return width;
3044         }
3045         dlg.footerEl.dom.style.display = '';
3046         for(var k in buttons){
3047             if(typeof buttons[k] != "function"){
3048                 if(b[k]){
3049                     buttons[k].show();
3050                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3051                     width += buttons[k].el.getWidth()+15;
3052                 }else{
3053                     buttons[k].hide();
3054                 }
3055             }
3056         }
3057         return width;
3058     };
3059
3060     // private
3061     var handleEsc = function(d, k, e){
3062         if(opt && opt.closable !== false){
3063             dlg.hide();
3064         }
3065         if(e){
3066             e.stopEvent();
3067         }
3068     };
3069
3070     return {
3071         /**
3072          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3073          * @return {Roo.BasicDialog} The BasicDialog element
3074          */
3075         getDialog : function(){
3076            if(!dlg){
3077                 dlg = new Roo.bootstrap.Modal( {
3078                     //draggable: true,
3079                     //resizable:false,
3080                     //constraintoviewport:false,
3081                     //fixedcenter:true,
3082                     //collapsible : false,
3083                     //shim:true,
3084                     //modal: true,
3085                 //    width: 'auto',
3086                   //  height:100,
3087                     //buttonAlign:"center",
3088                     closeClick : function(){
3089                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3090                             handleButton("no");
3091                         }else{
3092                             handleButton("cancel");
3093                         }
3094                     }
3095                 });
3096                 dlg.render();
3097                 dlg.on("hide", handleHide);
3098                 mask = dlg.mask;
3099                 //dlg.addKeyListener(27, handleEsc);
3100                 buttons = {};
3101                 this.buttons = buttons;
3102                 var bt = this.buttonText;
3103                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3104                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3105                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3106                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3107                 //Roo.log(buttons);
3108                 bodyEl = dlg.bodyEl.createChild({
3109
3110                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3111                         '<textarea class="roo-mb-textarea"></textarea>' +
3112                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3113                 });
3114                 msgEl = bodyEl.dom.firstChild;
3115                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3116                 textboxEl.enableDisplayMode();
3117                 textboxEl.addKeyListener([10,13], function(){
3118                     if(dlg.isVisible() && opt && opt.buttons){
3119                         if(opt.buttons.ok){
3120                             handleButton("ok");
3121                         }else if(opt.buttons.yes){
3122                             handleButton("yes");
3123                         }
3124                     }
3125                 });
3126                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3127                 textareaEl.enableDisplayMode();
3128                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3129                 progressEl.enableDisplayMode();
3130                 
3131                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3132                 var pf = progressEl.dom.firstChild;
3133                 if (pf) {
3134                     pp = Roo.get(pf.firstChild);
3135                     pp.setHeight(pf.offsetHeight);
3136                 }
3137                 
3138             }
3139             return dlg;
3140         },
3141
3142         /**
3143          * Updates the message box body text
3144          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3145          * the XHTML-compliant non-breaking space character '&amp;#160;')
3146          * @return {Roo.MessageBox} This message box
3147          */
3148         updateText : function(text)
3149         {
3150             if(!dlg.isVisible() && !opt.width){
3151                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3152                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3153             }
3154             msgEl.innerHTML = text || '&#160;';
3155       
3156             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3157             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3158             var w = Math.max(
3159                     Math.min(opt.width || cw , this.maxWidth), 
3160                     Math.max(opt.minWidth || this.minWidth, bwidth)
3161             );
3162             if(opt.prompt){
3163                 activeTextEl.setWidth(w);
3164             }
3165             if(dlg.isVisible()){
3166                 dlg.fixedcenter = false;
3167             }
3168             // to big, make it scroll. = But as usual stupid IE does not support
3169             // !important..
3170             
3171             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3172                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3173                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3174             } else {
3175                 bodyEl.dom.style.height = '';
3176                 bodyEl.dom.style.overflowY = '';
3177             }
3178             if (cw > w) {
3179                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3180             } else {
3181                 bodyEl.dom.style.overflowX = '';
3182             }
3183             
3184             dlg.setContentSize(w, bodyEl.getHeight());
3185             if(dlg.isVisible()){
3186                 dlg.fixedcenter = true;
3187             }
3188             return this;
3189         },
3190
3191         /**
3192          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3193          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3194          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3195          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3196          * @return {Roo.MessageBox} This message box
3197          */
3198         updateProgress : function(value, text){
3199             if(text){
3200                 this.updateText(text);
3201             }
3202             
3203             if (pp) { // weird bug on my firefox - for some reason this is not defined
3204                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3205                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3206             }
3207             return this;
3208         },        
3209
3210         /**
3211          * Returns true if the message box is currently displayed
3212          * @return {Boolean} True if the message box is visible, else false
3213          */
3214         isVisible : function(){
3215             return dlg && dlg.isVisible();  
3216         },
3217
3218         /**
3219          * Hides the message box if it is displayed
3220          */
3221         hide : function(){
3222             if(this.isVisible()){
3223                 dlg.hide();
3224             }  
3225         },
3226
3227         /**
3228          * Displays a new message box, or reinitializes an existing message box, based on the config options
3229          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3230          * The following config object properties are supported:
3231          * <pre>
3232 Property    Type             Description
3233 ----------  ---------------  ------------------------------------------------------------------------------------
3234 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3235                                    closes (defaults to undefined)
3236 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3237                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3238 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3239                                    progress and wait dialogs will ignore this property and always hide the
3240                                    close button as they can only be closed programmatically.
3241 cls               String           A custom CSS class to apply to the message box element
3242 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3243                                    displayed (defaults to 75)
3244 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3245                                    function will be btn (the name of the button that was clicked, if applicable,
3246                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3247                                    Progress and wait dialogs will ignore this option since they do not respond to
3248                                    user actions and can only be closed programmatically, so any required function
3249                                    should be called by the same code after it closes the dialog.
3250 icon              String           A CSS class that provides a background image to be used as an icon for
3251                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3252 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3253 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3254 modal             Boolean          False to allow user interaction with the page while the message box is
3255                                    displayed (defaults to true)
3256 msg               String           A string that will replace the existing message box body text (defaults
3257                                    to the XHTML-compliant non-breaking space character '&#160;')
3258 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3259 progress          Boolean          True to display a progress bar (defaults to false)
3260 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3261 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3262 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3263 title             String           The title text
3264 value             String           The string value to set into the active textbox element if displayed
3265 wait              Boolean          True to display a progress bar (defaults to false)
3266 width             Number           The width of the dialog in pixels
3267 </pre>
3268          *
3269          * Example usage:
3270          * <pre><code>
3271 Roo.Msg.show({
3272    title: 'Address',
3273    msg: 'Please enter your address:',
3274    width: 300,
3275    buttons: Roo.MessageBox.OKCANCEL,
3276    multiline: true,
3277    fn: saveAddress,
3278    animEl: 'addAddressBtn'
3279 });
3280 </code></pre>
3281          * @param {Object} config Configuration options
3282          * @return {Roo.MessageBox} This message box
3283          */
3284         show : function(options)
3285         {
3286             
3287             // this causes nightmares if you show one dialog after another
3288             // especially on callbacks..
3289              
3290             if(this.isVisible()){
3291                 
3292                 this.hide();
3293                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3294                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3295                 Roo.log("New Dialog Message:" +  options.msg )
3296                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3297                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3298                 
3299             }
3300             var d = this.getDialog();
3301             opt = options;
3302             d.setTitle(opt.title || "&#160;");
3303             d.closeEl.setDisplayed(opt.closable !== false);
3304             activeTextEl = textboxEl;
3305             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3306             if(opt.prompt){
3307                 if(opt.multiline){
3308                     textboxEl.hide();
3309                     textareaEl.show();
3310                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3311                         opt.multiline : this.defaultTextHeight);
3312                     activeTextEl = textareaEl;
3313                 }else{
3314                     textboxEl.show();
3315                     textareaEl.hide();
3316                 }
3317             }else{
3318                 textboxEl.hide();
3319                 textareaEl.hide();
3320             }
3321             progressEl.setDisplayed(opt.progress === true);
3322             this.updateProgress(0);
3323             activeTextEl.dom.value = opt.value || "";
3324             if(opt.prompt){
3325                 dlg.setDefaultButton(activeTextEl);
3326             }else{
3327                 var bs = opt.buttons;
3328                 var db = null;
3329                 if(bs && bs.ok){
3330                     db = buttons["ok"];
3331                 }else if(bs && bs.yes){
3332                     db = buttons["yes"];
3333                 }
3334                 dlg.setDefaultButton(db);
3335             }
3336             bwidth = updateButtons(opt.buttons);
3337             this.updateText(opt.msg);
3338             if(opt.cls){
3339                 d.el.addClass(opt.cls);
3340             }
3341             d.proxyDrag = opt.proxyDrag === true;
3342             d.modal = opt.modal !== false;
3343             d.mask = opt.modal !== false ? mask : false;
3344             if(!d.isVisible()){
3345                 // force it to the end of the z-index stack so it gets a cursor in FF
3346                 document.body.appendChild(dlg.el.dom);
3347                 d.animateTarget = null;
3348                 d.show(options.animEl);
3349             }
3350             return this;
3351         },
3352
3353         /**
3354          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3355          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3356          * and closing the message box when the process is complete.
3357          * @param {String} title The title bar text
3358          * @param {String} msg The message box body text
3359          * @return {Roo.MessageBox} This message box
3360          */
3361         progress : function(title, msg){
3362             this.show({
3363                 title : title,
3364                 msg : msg,
3365                 buttons: false,
3366                 progress:true,
3367                 closable:false,
3368                 minWidth: this.minProgressWidth,
3369                 modal : true
3370             });
3371             return this;
3372         },
3373
3374         /**
3375          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3376          * If a callback function is passed it will be called after the user clicks the button, and the
3377          * id of the button that was clicked will be passed as the only parameter to the callback
3378          * (could also be the top-right close button).
3379          * @param {String} title The title bar text
3380          * @param {String} msg The message box body text
3381          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3382          * @param {Object} scope (optional) The scope of the callback function
3383          * @return {Roo.MessageBox} This message box
3384          */
3385         alert : function(title, msg, fn, scope)
3386         {
3387             this.show({
3388                 title : title,
3389                 msg : msg,
3390                 buttons: this.OK,
3391                 fn: fn,
3392                 closable : false,
3393                 scope : scope,
3394                 modal : true
3395             });
3396             return this;
3397         },
3398
3399         /**
3400          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3401          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3402          * You are responsible for closing the message box when the process is complete.
3403          * @param {String} msg The message box body text
3404          * @param {String} title (optional) The title bar text
3405          * @return {Roo.MessageBox} This message box
3406          */
3407         wait : function(msg, title){
3408             this.show({
3409                 title : title,
3410                 msg : msg,
3411                 buttons: false,
3412                 closable:false,
3413                 progress:true,
3414                 modal:true,
3415                 width:300,
3416                 wait:true
3417             });
3418             waitTimer = Roo.TaskMgr.start({
3419                 run: function(i){
3420                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3421                 },
3422                 interval: 1000
3423             });
3424             return this;
3425         },
3426
3427         /**
3428          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3429          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3430          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3431          * @param {String} title The title bar text
3432          * @param {String} msg The message box body text
3433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3434          * @param {Object} scope (optional) The scope of the callback function
3435          * @return {Roo.MessageBox} This message box
3436          */
3437         confirm : function(title, msg, fn, scope){
3438             this.show({
3439                 title : title,
3440                 msg : msg,
3441                 buttons: this.YESNO,
3442                 fn: fn,
3443                 scope : scope,
3444                 modal : true
3445             });
3446             return this;
3447         },
3448
3449         /**
3450          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3451          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3452          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3453          * (could also be the top-right close button) and the text that was entered will be passed as the two
3454          * parameters to the callback.
3455          * @param {String} title The title bar text
3456          * @param {String} msg The message box body text
3457          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3458          * @param {Object} scope (optional) The scope of the callback function
3459          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3460          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3461          * @return {Roo.MessageBox} This message box
3462          */
3463         prompt : function(title, msg, fn, scope, multiline){
3464             this.show({
3465                 title : title,
3466                 msg : msg,
3467                 buttons: this.OKCANCEL,
3468                 fn: fn,
3469                 minWidth:250,
3470                 scope : scope,
3471                 prompt:true,
3472                 multiline: multiline,
3473                 modal : true
3474             });
3475             return this;
3476         },
3477
3478         /**
3479          * Button config that displays a single OK button
3480          * @type Object
3481          */
3482         OK : {ok:true},
3483         /**
3484          * Button config that displays Yes and No buttons
3485          * @type Object
3486          */
3487         YESNO : {yes:true, no:true},
3488         /**
3489          * Button config that displays OK and Cancel buttons
3490          * @type Object
3491          */
3492         OKCANCEL : {ok:true, cancel:true},
3493         /**
3494          * Button config that displays Yes, No and Cancel buttons
3495          * @type Object
3496          */
3497         YESNOCANCEL : {yes:true, no:true, cancel:true},
3498
3499         /**
3500          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3501          * @type Number
3502          */
3503         defaultTextHeight : 75,
3504         /**
3505          * The maximum width in pixels of the message box (defaults to 600)
3506          * @type Number
3507          */
3508         maxWidth : 600,
3509         /**
3510          * The minimum width in pixels of the message box (defaults to 100)
3511          * @type Number
3512          */
3513         minWidth : 100,
3514         /**
3515          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3516          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3517          * @type Number
3518          */
3519         minProgressWidth : 250,
3520         /**
3521          * An object containing the default button text strings that can be overriden for localized language support.
3522          * Supported properties are: ok, cancel, yes and no.
3523          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3524          * @type Object
3525          */
3526         buttonText : {
3527             ok : "OK",
3528             cancel : "Cancel",
3529             yes : "Yes",
3530             no : "No"
3531         }
3532     };
3533 }();
3534
3535 /**
3536  * Shorthand for {@link Roo.MessageBox}
3537  */
3538 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3539 Roo.Msg = Roo.Msg || Roo.MessageBox;
3540 /*
3541  * - LGPL
3542  *
3543  * navbar
3544  * 
3545  */
3546
3547 /**
3548  * @class Roo.bootstrap.Navbar
3549  * @extends Roo.bootstrap.Component
3550  * Bootstrap Navbar class
3551
3552  * @constructor
3553  * Create a new Navbar
3554  * @param {Object} config The config object
3555  */
3556
3557
3558 Roo.bootstrap.Navbar = function(config){
3559     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3560     this.addEvents({
3561         // raw events
3562         /**
3563          * @event beforetoggle
3564          * Fire before toggle the menu
3565          * @param {Roo.EventObject} e
3566          */
3567         "beforetoggle" : true
3568     });
3569 };
3570
3571 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3572     
3573     
3574    
3575     // private
3576     navItems : false,
3577     loadMask : false,
3578     
3579     
3580     getAutoCreate : function(){
3581         
3582         
3583         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3584         
3585     },
3586     
3587     initEvents :function ()
3588     {
3589         //Roo.log(this.el.select('.navbar-toggle',true));
3590         this.el.select('.navbar-toggle',true).on('click', function() {
3591             if(this.fireEvent('beforetoggle', this) !== false){
3592                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3593             }
3594             
3595         }, this);
3596         
3597         var mark = {
3598             tag: "div",
3599             cls:"x-dlg-mask"
3600         };
3601         
3602         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3603         
3604         var size = this.el.getSize();
3605         this.maskEl.setSize(size.width, size.height);
3606         this.maskEl.enableDisplayMode("block");
3607         this.maskEl.hide();
3608         
3609         if(this.loadMask){
3610             this.maskEl.show();
3611         }
3612     },
3613     
3614     
3615     getChildContainer : function()
3616     {
3617         if (this.el.select('.collapse').getCount()) {
3618             return this.el.select('.collapse',true).first();
3619         }
3620         
3621         return this.el;
3622     },
3623     
3624     mask : function()
3625     {
3626         this.maskEl.show();
3627     },
3628     
3629     unmask : function()
3630     {
3631         this.maskEl.hide();
3632     } 
3633     
3634     
3635     
3636     
3637 });
3638
3639
3640
3641  
3642
3643  /*
3644  * - LGPL
3645  *
3646  * navbar
3647  * 
3648  */
3649
3650 /**
3651  * @class Roo.bootstrap.NavSimplebar
3652  * @extends Roo.bootstrap.Navbar
3653  * Bootstrap Sidebar class
3654  *
3655  * @cfg {Boolean} inverse is inverted color
3656  * 
3657  * @cfg {String} type (nav | pills | tabs)
3658  * @cfg {Boolean} arrangement stacked | justified
3659  * @cfg {String} align (left | right) alignment
3660  * 
3661  * @cfg {Boolean} main (true|false) main nav bar? default false
3662  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3663  * 
3664  * @cfg {String} tag (header|footer|nav|div) default is nav 
3665
3666  * 
3667  * 
3668  * 
3669  * @constructor
3670  * Create a new Sidebar
3671  * @param {Object} config The config object
3672  */
3673
3674
3675 Roo.bootstrap.NavSimplebar = function(config){
3676     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3677 };
3678
3679 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3680     
3681     inverse: false,
3682     
3683     type: false,
3684     arrangement: '',
3685     align : false,
3686     
3687     
3688     
3689     main : false,
3690     
3691     
3692     tag : false,
3693     
3694     
3695     getAutoCreate : function(){
3696         
3697         
3698         var cfg = {
3699             tag : this.tag || 'div',
3700             cls : 'navbar'
3701         };
3702           
3703         
3704         cfg.cn = [
3705             {
3706                 cls: 'nav',
3707                 tag : 'ul'
3708             }
3709         ];
3710         
3711          
3712         this.type = this.type || 'nav';
3713         if (['tabs','pills'].indexOf(this.type)!==-1) {
3714             cfg.cn[0].cls += ' nav-' + this.type
3715         
3716         
3717         } else {
3718             if (this.type!=='nav') {
3719                 Roo.log('nav type must be nav/tabs/pills')
3720             }
3721             cfg.cn[0].cls += ' navbar-nav'
3722         }
3723         
3724         
3725         
3726         
3727         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3728             cfg.cn[0].cls += ' nav-' + this.arrangement;
3729         }
3730         
3731         
3732         if (this.align === 'right') {
3733             cfg.cn[0].cls += ' navbar-right';
3734         }
3735         
3736         if (this.inverse) {
3737             cfg.cls += ' navbar-inverse';
3738             
3739         }
3740         
3741         
3742         return cfg;
3743     
3744         
3745     }
3746     
3747     
3748     
3749 });
3750
3751
3752
3753  
3754
3755  
3756        /*
3757  * - LGPL
3758  *
3759  * navbar
3760  * 
3761  */
3762
3763 /**
3764  * @class Roo.bootstrap.NavHeaderbar
3765  * @extends Roo.bootstrap.NavSimplebar
3766  * Bootstrap Sidebar class
3767  *
3768  * @cfg {String} brand what is brand
3769  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3770  * @cfg {String} brand_href href of the brand
3771  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3772  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3773  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3774  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3775  * 
3776  * @constructor
3777  * Create a new Sidebar
3778  * @param {Object} config The config object
3779  */
3780
3781
3782 Roo.bootstrap.NavHeaderbar = function(config){
3783     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3784       
3785 };
3786
3787 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3788     
3789     position: '',
3790     brand: '',
3791     brand_href: false,
3792     srButton : true,
3793     autohide : false,
3794     desktopCenter : false,
3795    
3796     
3797     getAutoCreate : function(){
3798         
3799         var   cfg = {
3800             tag: this.nav || 'nav',
3801             cls: 'navbar',
3802             role: 'navigation',
3803             cn: []
3804         };
3805         
3806         var cn = cfg.cn;
3807         if (this.desktopCenter) {
3808             cn.push({cls : 'container', cn : []});
3809             cn = cn[0].cn;
3810         }
3811         
3812         if(this.srButton){
3813             cn.push({
3814                 tag: 'div',
3815                 cls: 'navbar-header',
3816                 cn: [
3817                     {
3818                         tag: 'button',
3819                         type: 'button',
3820                         cls: 'navbar-toggle',
3821                         'data-toggle': 'collapse',
3822                         cn: [
3823                             {
3824                                 tag: 'span',
3825                                 cls: 'sr-only',
3826                                 html: 'Toggle navigation'
3827                             },
3828                             {
3829                                 tag: 'span',
3830                                 cls: 'icon-bar'
3831                             },
3832                             {
3833                                 tag: 'span',
3834                                 cls: 'icon-bar'
3835                             },
3836                             {
3837                                 tag: 'span',
3838                                 cls: 'icon-bar'
3839                             }
3840                         ]
3841                     }
3842                 ]
3843             });
3844         }
3845         
3846         cn.push({
3847             tag: 'div',
3848             cls: 'collapse navbar-collapse',
3849             cn : []
3850         });
3851         
3852         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3853         
3854         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3855             cfg.cls += ' navbar-' + this.position;
3856             
3857             // tag can override this..
3858             
3859             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3860         }
3861         
3862         if (this.brand !== '') {
3863             cn[0].cn.push({
3864                 tag: 'a',
3865                 href: this.brand_href ? this.brand_href : '#',
3866                 cls: 'navbar-brand',
3867                 cn: [
3868                 this.brand
3869                 ]
3870             });
3871         }
3872         
3873         if(this.main){
3874             cfg.cls += ' main-nav';
3875         }
3876         
3877         
3878         return cfg;
3879
3880         
3881     },
3882     getHeaderChildContainer : function()
3883     {
3884         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3885             return this.el.select('.navbar-header',true).first();
3886         }
3887         
3888         return this.getChildContainer();
3889     },
3890     
3891     
3892     initEvents : function()
3893     {
3894         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3895         
3896         if (this.autohide) {
3897             
3898             var prevScroll = 0;
3899             var ft = this.el;
3900             
3901             Roo.get(document).on('scroll',function(e) {
3902                 var ns = Roo.get(document).getScroll().top;
3903                 var os = prevScroll;
3904                 prevScroll = ns;
3905                 
3906                 if(ns > os){
3907                     ft.removeClass('slideDown');
3908                     ft.addClass('slideUp');
3909                     return;
3910                 }
3911                 ft.removeClass('slideUp');
3912                 ft.addClass('slideDown');
3913                  
3914               
3915           },this);
3916         }
3917     }    
3918     
3919 });
3920
3921
3922
3923  
3924
3925  /*
3926  * - LGPL
3927  *
3928  * navbar
3929  * 
3930  */
3931
3932 /**
3933  * @class Roo.bootstrap.NavSidebar
3934  * @extends Roo.bootstrap.Navbar
3935  * Bootstrap Sidebar class
3936  * 
3937  * @constructor
3938  * Create a new Sidebar
3939  * @param {Object} config The config object
3940  */
3941
3942
3943 Roo.bootstrap.NavSidebar = function(config){
3944     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3945 };
3946
3947 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3948     
3949     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3950     
3951     getAutoCreate : function(){
3952         
3953         
3954         return  {
3955             tag: 'div',
3956             cls: 'sidebar sidebar-nav'
3957         };
3958     
3959         
3960     }
3961     
3962     
3963     
3964 });
3965
3966
3967
3968  
3969
3970  /*
3971  * - LGPL
3972  *
3973  * nav group
3974  * 
3975  */
3976
3977 /**
3978  * @class Roo.bootstrap.NavGroup
3979  * @extends Roo.bootstrap.Component
3980  * Bootstrap NavGroup class
3981  * @cfg {String} align (left|right)
3982  * @cfg {Boolean} inverse
3983  * @cfg {String} type (nav|pills|tab) default nav
3984  * @cfg {String} navId - reference Id for navbar.
3985
3986  * 
3987  * @constructor
3988  * Create a new nav group
3989  * @param {Object} config The config object
3990  */
3991
3992 Roo.bootstrap.NavGroup = function(config){
3993     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3994     this.navItems = [];
3995    
3996     Roo.bootstrap.NavGroup.register(this);
3997      this.addEvents({
3998         /**
3999              * @event changed
4000              * Fires when the active item changes
4001              * @param {Roo.bootstrap.NavGroup} this
4002              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4003              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4004          */
4005         'changed': true
4006      });
4007     
4008 };
4009
4010 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4011     
4012     align: '',
4013     inverse: false,
4014     form: false,
4015     type: 'nav',
4016     navId : '',
4017     // private
4018     
4019     navItems : false, 
4020     
4021     getAutoCreate : function()
4022     {
4023         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4024         
4025         cfg = {
4026             tag : 'ul',
4027             cls: 'nav' 
4028         };
4029         
4030         if (['tabs','pills'].indexOf(this.type)!==-1) {
4031             cfg.cls += ' nav-' + this.type
4032         } else {
4033             if (this.type!=='nav') {
4034                 Roo.log('nav type must be nav/tabs/pills')
4035             }
4036             cfg.cls += ' navbar-nav'
4037         }
4038         
4039         if (this.parent() && this.parent().sidebar) {
4040             cfg = {
4041                 tag: 'ul',
4042                 cls: 'dashboard-menu sidebar-menu'
4043             };
4044             
4045             return cfg;
4046         }
4047         
4048         if (this.form === true) {
4049             cfg = {
4050                 tag: 'form',
4051                 cls: 'navbar-form'
4052             };
4053             
4054             if (this.align === 'right') {
4055                 cfg.cls += ' navbar-right';
4056             } else {
4057                 cfg.cls += ' navbar-left';
4058             }
4059         }
4060         
4061         if (this.align === 'right') {
4062             cfg.cls += ' navbar-right';
4063         }
4064         
4065         if (this.inverse) {
4066             cfg.cls += ' navbar-inverse';
4067             
4068         }
4069         
4070         
4071         return cfg;
4072     },
4073     /**
4074     * sets the active Navigation item
4075     * @param {Roo.bootstrap.NavItem} the new current navitem
4076     */
4077     setActiveItem : function(item)
4078     {
4079         var prev = false;
4080         Roo.each(this.navItems, function(v){
4081             if (v == item) {
4082                 return ;
4083             }
4084             if (v.isActive()) {
4085                 v.setActive(false, true);
4086                 prev = v;
4087                 
4088             }
4089             
4090         });
4091
4092         item.setActive(true, true);
4093         this.fireEvent('changed', this, item, prev);
4094         
4095         
4096     },
4097     /**
4098     * gets the active Navigation item
4099     * @return {Roo.bootstrap.NavItem} the current navitem
4100     */
4101     getActive : function()
4102     {
4103         
4104         var prev = false;
4105         Roo.each(this.navItems, function(v){
4106             
4107             if (v.isActive()) {
4108                 prev = v;
4109                 
4110             }
4111             
4112         });
4113         return prev;
4114     },
4115     
4116     indexOfNav : function()
4117     {
4118         
4119         var prev = false;
4120         Roo.each(this.navItems, function(v,i){
4121             
4122             if (v.isActive()) {
4123                 prev = i;
4124                 
4125             }
4126             
4127         });
4128         return prev;
4129     },
4130     /**
4131     * adds a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     addItem : function(cfg)
4135     {
4136         var cn = new Roo.bootstrap.NavItem(cfg);
4137         this.register(cn);
4138         cn.parentId = this.id;
4139         cn.onRender(this.el, null);
4140         return cn;
4141     },
4142     /**
4143     * register a Navigation item
4144     * @param {Roo.bootstrap.NavItem} the navitem to add
4145     */
4146     register : function(item)
4147     {
4148         this.navItems.push( item);
4149         item.navId = this.navId;
4150     
4151     },
4152     
4153     /**
4154     * clear all the Navigation item
4155     */
4156    
4157     clearAll : function()
4158     {
4159         this.navItems = [];
4160         this.el.dom.innerHTML = '';
4161     },
4162     
4163     getNavItem: function(tabId)
4164     {
4165         var ret = false;
4166         Roo.each(this.navItems, function(e) {
4167             if (e.tabId == tabId) {
4168                ret =  e;
4169                return false;
4170             }
4171             return true;
4172             
4173         });
4174         return ret;
4175     },
4176     
4177     setActiveNext : function()
4178     {
4179         var i = this.indexOfNav(this.getActive());
4180         if (i > this.navItems.length) {
4181             return;
4182         }
4183         this.setActiveItem(this.navItems[i+1]);
4184     },
4185     setActivePrev : function()
4186     {
4187         var i = this.indexOfNav(this.getActive());
4188         if (i  < 1) {
4189             return;
4190         }
4191         this.setActiveItem(this.navItems[i-1]);
4192     },
4193     clearWasActive : function(except) {
4194         Roo.each(this.navItems, function(e) {
4195             if (e.tabId != except.tabId && e.was_active) {
4196                e.was_active = false;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202     },
4203     getWasActive : function ()
4204     {
4205         var r = false;
4206         Roo.each(this.navItems, function(e) {
4207             if (e.was_active) {
4208                r = e;
4209                return false;
4210             }
4211             return true;
4212             
4213         });
4214         return r;
4215     }
4216     
4217     
4218 });
4219
4220  
4221 Roo.apply(Roo.bootstrap.NavGroup, {
4222     
4223     groups: {},
4224      /**
4225     * register a Navigation Group
4226     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4227     */
4228     register : function(navgrp)
4229     {
4230         this.groups[navgrp.navId] = navgrp;
4231         
4232     },
4233     /**
4234     * fetch a Navigation Group based on the navigation ID
4235     * @param {string} the navgroup to add
4236     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4237     */
4238     get: function(navId) {
4239         if (typeof(this.groups[navId]) == 'undefined') {
4240             return false;
4241             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4242         }
4243         return this.groups[navId] ;
4244     }
4245     
4246     
4247     
4248 });
4249
4250  /*
4251  * - LGPL
4252  *
4253  * row
4254  * 
4255  */
4256
4257 /**
4258  * @class Roo.bootstrap.NavItem
4259  * @extends Roo.bootstrap.Component
4260  * Bootstrap Navbar.NavItem class
4261  * @cfg {String} href  link to
4262  * @cfg {String} html content of button
4263  * @cfg {String} badge text inside badge
4264  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4265  * @cfg {String} glyphicon name of glyphicon
4266  * @cfg {String} icon name of font awesome icon
4267  * @cfg {Boolean} active Is item active
4268  * @cfg {Boolean} disabled Is item disabled
4269  
4270  * @cfg {Boolean} preventDefault (true | false) default false
4271  * @cfg {String} tabId the tab that this item activates.
4272  * @cfg {String} tagtype (a|span) render as a href or span?
4273  * @cfg {Boolean} animateRef (true|false) link to element default false  
4274   
4275  * @constructor
4276  * Create a new Navbar Item
4277  * @param {Object} config The config object
4278  */
4279 Roo.bootstrap.NavItem = function(config){
4280     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4281     this.addEvents({
4282         // raw events
4283         /**
4284          * @event click
4285          * The raw click event for the entire grid.
4286          * @param {Roo.EventObject} e
4287          */
4288         "click" : true,
4289          /**
4290             * @event changed
4291             * Fires when the active item active state changes
4292             * @param {Roo.bootstrap.NavItem} this
4293             * @param {boolean} state the new state
4294              
4295          */
4296         'changed': true,
4297         /**
4298             * @event scrollto
4299             * Fires when scroll to element
4300             * @param {Roo.bootstrap.NavItem} this
4301             * @param {Object} options
4302             * @param {Roo.EventObject} e
4303              
4304          */
4305         'scrollto': true
4306     });
4307    
4308 };
4309
4310 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4311     
4312     href: false,
4313     html: '',
4314     badge: '',
4315     icon: false,
4316     glyphicon: false,
4317     active: false,
4318     preventDefault : false,
4319     tabId : false,
4320     tagtype : 'a',
4321     disabled : false,
4322     animateRef : false,
4323     was_active : false,
4324     
4325     getAutoCreate : function(){
4326          
4327         var cfg = {
4328             tag: 'li',
4329             cls: 'nav-item'
4330             
4331         };
4332         
4333         if (this.active) {
4334             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4335         }
4336         if (this.disabled) {
4337             cfg.cls += ' disabled';
4338         }
4339         
4340         if (this.href || this.html || this.glyphicon || this.icon) {
4341             cfg.cn = [
4342                 {
4343                     tag: this.tagtype,
4344                     href : this.href || "#",
4345                     html: this.html || ''
4346                 }
4347             ];
4348             
4349             if (this.icon) {
4350                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4351             }
4352
4353             if(this.glyphicon) {
4354                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4355             }
4356             
4357             if (this.menu) {
4358                 
4359                 cfg.cn[0].html += " <span class='caret'></span>";
4360              
4361             }
4362             
4363             if (this.badge !== '') {
4364                  
4365                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4366             }
4367         }
4368         
4369         
4370         
4371         return cfg;
4372     },
4373     initEvents: function() 
4374     {
4375         if (typeof (this.menu) != 'undefined') {
4376             this.menu.parentType = this.xtype;
4377             this.menu.triggerEl = this.el;
4378             this.menu = this.addxtype(Roo.apply({}, this.menu));
4379         }
4380         
4381         this.el.select('a',true).on('click', this.onClick, this);
4382         
4383         if(this.tagtype == 'span'){
4384             this.el.select('span',true).on('click', this.onClick, this);
4385         }
4386        
4387         // at this point parent should be available..
4388         this.parent().register(this);
4389     },
4390     
4391     onClick : function(e)
4392     {
4393         if (e.getTarget('.dropdown-menu-item')) {
4394             // did you click on a menu itemm.... - then don't trigger onclick..
4395             return;
4396         }
4397         
4398         if(
4399                 this.preventDefault || 
4400                 this.href == '#' 
4401         ){
4402             Roo.log("NavItem - prevent Default?");
4403             e.preventDefault();
4404         }
4405         
4406         if (this.disabled) {
4407             return;
4408         }
4409         
4410         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4411         if (tg && tg.transition) {
4412             Roo.log("waiting for the transitionend");
4413             return;
4414         }
4415         
4416         
4417         
4418         //Roo.log("fire event clicked");
4419         if(this.fireEvent('click', this, e) === false){
4420             return;
4421         };
4422         
4423         if(this.tagtype == 'span'){
4424             return;
4425         }
4426         
4427         //Roo.log(this.href);
4428         var ael = this.el.select('a',true).first();
4429         //Roo.log(ael);
4430         
4431         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4432             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4433             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4434                 return; // ignore... - it's a 'hash' to another page.
4435             }
4436             Roo.log("NavItem - prevent Default?");
4437             e.preventDefault();
4438             this.scrollToElement(e);
4439         }
4440         
4441         
4442         var p =  this.parent();
4443    
4444         if (['tabs','pills'].indexOf(p.type)!==-1) {
4445             if (typeof(p.setActiveItem) !== 'undefined') {
4446                 p.setActiveItem(this);
4447             }
4448         }
4449         
4450         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4451         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4452             // remove the collapsed menu expand...
4453             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4454         }
4455     },
4456     
4457     isActive: function () {
4458         return this.active
4459     },
4460     setActive : function(state, fire, is_was_active)
4461     {
4462         if (this.active && !state && this.navId) {
4463             this.was_active = true;
4464             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4465             if (nv) {
4466                 nv.clearWasActive(this);
4467             }
4468             
4469         }
4470         this.active = state;
4471         
4472         if (!state ) {
4473             this.el.removeClass('active');
4474         } else if (!this.el.hasClass('active')) {
4475             this.el.addClass('active');
4476         }
4477         if (fire) {
4478             this.fireEvent('changed', this, state);
4479         }
4480         
4481         // show a panel if it's registered and related..
4482         
4483         if (!this.navId || !this.tabId || !state || is_was_active) {
4484             return;
4485         }
4486         
4487         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4488         if (!tg) {
4489             return;
4490         }
4491         var pan = tg.getPanelByName(this.tabId);
4492         if (!pan) {
4493             return;
4494         }
4495         // if we can not flip to new panel - go back to old nav highlight..
4496         if (false == tg.showPanel(pan)) {
4497             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4498             if (nv) {
4499                 var onav = nv.getWasActive();
4500                 if (onav) {
4501                     onav.setActive(true, false, true);
4502                 }
4503             }
4504             
4505         }
4506         
4507         
4508         
4509     },
4510      // this should not be here...
4511     setDisabled : function(state)
4512     {
4513         this.disabled = state;
4514         if (!state ) {
4515             this.el.removeClass('disabled');
4516         } else if (!this.el.hasClass('disabled')) {
4517             this.el.addClass('disabled');
4518         }
4519         
4520     },
4521     
4522     /**
4523      * Fetch the element to display the tooltip on.
4524      * @return {Roo.Element} defaults to this.el
4525      */
4526     tooltipEl : function()
4527     {
4528         return this.el.select('' + this.tagtype + '', true).first();
4529     },
4530     
4531     scrollToElement : function(e)
4532     {
4533         var c = document.body;
4534         
4535         /*
4536          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4537          */
4538         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4539             c = document.documentElement;
4540         }
4541         
4542         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4543         
4544         if(!target){
4545             return;
4546         }
4547
4548         var o = target.calcOffsetsTo(c);
4549         
4550         var options = {
4551             target : target,
4552             value : o[1]
4553         };
4554         
4555         this.fireEvent('scrollto', this, options, e);
4556         
4557         Roo.get(c).scrollTo('top', options.value, true);
4558         
4559         return;
4560     }
4561 });
4562  
4563
4564  /*
4565  * - LGPL
4566  *
4567  * sidebar item
4568  *
4569  *  li
4570  *    <span> icon </span>
4571  *    <span> text </span>
4572  *    <span>badge </span>
4573  */
4574
4575 /**
4576  * @class Roo.bootstrap.NavSidebarItem
4577  * @extends Roo.bootstrap.NavItem
4578  * Bootstrap Navbar.NavSidebarItem class
4579  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4580  * {Boolean} open is the menu open
4581  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4582  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4583  * {String} buttonSize (sm|md|lg)the extra classes for the button
4584  * {Boolean} showArrow show arrow next to the text (default true)
4585  * @constructor
4586  * Create a new Navbar Button
4587  * @param {Object} config The config object
4588  */
4589 Roo.bootstrap.NavSidebarItem = function(config){
4590     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4591     this.addEvents({
4592         // raw events
4593         /**
4594          * @event click
4595          * The raw click event for the entire grid.
4596          * @param {Roo.EventObject} e
4597          */
4598         "click" : true,
4599          /**
4600             * @event changed
4601             * Fires when the active item active state changes
4602             * @param {Roo.bootstrap.NavSidebarItem} this
4603             * @param {boolean} state the new state
4604              
4605          */
4606         'changed': true
4607     });
4608    
4609 };
4610
4611 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4612     
4613     badgeWeight : 'default',
4614     
4615     open: false,
4616     
4617     buttonView : false,
4618     
4619     buttonWeight : 'default',
4620     
4621     buttonSize : 'md',
4622     
4623     showArrow : true,
4624     
4625     getAutoCreate : function(){
4626         
4627         
4628         var a = {
4629                 tag: 'a',
4630                 href : this.href || '#',
4631                 cls: '',
4632                 html : '',
4633                 cn : []
4634         };
4635         
4636         if(this.buttonView){
4637             a = {
4638                 tag: 'button',
4639                 href : this.href || '#',
4640                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4641                 html : this.html,
4642                 cn : []
4643             };
4644         }
4645         
4646         var cfg = {
4647             tag: 'li',
4648             cls: '',
4649             cn: [ a ]
4650         };
4651         
4652         if (this.active) {
4653             cfg.cls += ' active';
4654         }
4655         
4656         if (this.disabled) {
4657             cfg.cls += ' disabled';
4658         }
4659         if (this.open) {
4660             cfg.cls += ' open x-open';
4661         }
4662         // left icon..
4663         if (this.glyphicon || this.icon) {
4664             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4665             a.cn.push({ tag : 'i', cls : c }) ;
4666         }
4667         
4668         if(!this.buttonView){
4669             var span = {
4670                 tag: 'span',
4671                 html : this.html || ''
4672             };
4673
4674             a.cn.push(span);
4675             
4676         }
4677         
4678         if (this.badge !== '') {
4679             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4680         }
4681         
4682         if (this.menu) {
4683             
4684             if(this.showArrow){
4685                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4686             }
4687             
4688             a.cls += ' dropdown-toggle treeview' ;
4689         }
4690         
4691         return cfg;
4692     },
4693     
4694     initEvents : function()
4695     { 
4696         if (typeof (this.menu) != 'undefined') {
4697             this.menu.parentType = this.xtype;
4698             this.menu.triggerEl = this.el;
4699             this.menu = this.addxtype(Roo.apply({}, this.menu));
4700         }
4701         
4702         this.el.on('click', this.onClick, this);
4703         
4704         if(this.badge !== ''){
4705             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4706         }
4707         
4708     },
4709     
4710     onClick : function(e)
4711     {
4712         if(this.disabled){
4713             e.preventDefault();
4714             return;
4715         }
4716         
4717         if(this.preventDefault){
4718             e.preventDefault();
4719         }
4720         
4721         this.fireEvent('click', this);
4722     },
4723     
4724     disable : function()
4725     {
4726         this.setDisabled(true);
4727     },
4728     
4729     enable : function()
4730     {
4731         this.setDisabled(false);
4732     },
4733     
4734     setDisabled : function(state)
4735     {
4736         if(this.disabled == state){
4737             return;
4738         }
4739         
4740         this.disabled = state;
4741         
4742         if (state) {
4743             this.el.addClass('disabled');
4744             return;
4745         }
4746         
4747         this.el.removeClass('disabled');
4748         
4749         return;
4750     },
4751     
4752     setActive : function(state)
4753     {
4754         if(this.active == state){
4755             return;
4756         }
4757         
4758         this.active = state;
4759         
4760         if (state) {
4761             this.el.addClass('active');
4762             return;
4763         }
4764         
4765         this.el.removeClass('active');
4766         
4767         return;
4768     },
4769     
4770     isActive: function () 
4771     {
4772         return this.active;
4773     },
4774     
4775     setBadge : function(str)
4776     {
4777         if(!this.badgeEl){
4778             return;
4779         }
4780         
4781         this.badgeEl.dom.innerHTML = str;
4782     }
4783     
4784    
4785      
4786  
4787 });
4788  
4789
4790  /*
4791  * - LGPL
4792  *
4793  * row
4794  * 
4795  */
4796
4797 /**
4798  * @class Roo.bootstrap.Row
4799  * @extends Roo.bootstrap.Component
4800  * Bootstrap Row class (contains columns...)
4801  * 
4802  * @constructor
4803  * Create a new Row
4804  * @param {Object} config The config object
4805  */
4806
4807 Roo.bootstrap.Row = function(config){
4808     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4809 };
4810
4811 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4812     
4813     getAutoCreate : function(){
4814        return {
4815             cls: 'row clearfix'
4816        };
4817     }
4818     
4819     
4820 });
4821
4822  
4823
4824  /*
4825  * - LGPL
4826  *
4827  * element
4828  * 
4829  */
4830
4831 /**
4832  * @class Roo.bootstrap.Element
4833  * @extends Roo.bootstrap.Component
4834  * Bootstrap Element class
4835  * @cfg {String} html contents of the element
4836  * @cfg {String} tag tag of the element
4837  * @cfg {String} cls class of the element
4838  * @cfg {Boolean} preventDefault (true|false) default false
4839  * @cfg {Boolean} clickable (true|false) default false
4840  * 
4841  * @constructor
4842  * Create a new Element
4843  * @param {Object} config The config object
4844  */
4845
4846 Roo.bootstrap.Element = function(config){
4847     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4848     
4849     this.addEvents({
4850         // raw events
4851         /**
4852          * @event click
4853          * When a element is chick
4854          * @param {Roo.bootstrap.Element} this
4855          * @param {Roo.EventObject} e
4856          */
4857         "click" : true
4858     });
4859 };
4860
4861 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4862     
4863     tag: 'div',
4864     cls: '',
4865     html: '',
4866     preventDefault: false, 
4867     clickable: false,
4868     
4869     getAutoCreate : function(){
4870         
4871         var cfg = {
4872             tag: this.tag,
4873             cls: this.cls,
4874             html: this.html
4875         };
4876         
4877         return cfg;
4878     },
4879     
4880     initEvents: function() 
4881     {
4882         Roo.bootstrap.Element.superclass.initEvents.call(this);
4883         
4884         if(this.clickable){
4885             this.el.on('click', this.onClick, this);
4886         }
4887         
4888     },
4889     
4890     onClick : function(e)
4891     {
4892         if(this.preventDefault){
4893             e.preventDefault();
4894         }
4895         
4896         this.fireEvent('click', this, e);
4897     },
4898     
4899     getValue : function()
4900     {
4901         return this.el.dom.innerHTML;
4902     },
4903     
4904     setValue : function(value)
4905     {
4906         this.el.dom.innerHTML = value;
4907     }
4908    
4909 });
4910
4911  
4912
4913  /*
4914  * - LGPL
4915  *
4916  * pagination
4917  * 
4918  */
4919
4920 /**
4921  * @class Roo.bootstrap.Pagination
4922  * @extends Roo.bootstrap.Component
4923  * Bootstrap Pagination class
4924  * @cfg {String} size xs | sm | md | lg
4925  * @cfg {Boolean} inverse false | true
4926  * 
4927  * @constructor
4928  * Create a new Pagination
4929  * @param {Object} config The config object
4930  */
4931
4932 Roo.bootstrap.Pagination = function(config){
4933     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4934 };
4935
4936 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4937     
4938     cls: false,
4939     size: false,
4940     inverse: false,
4941     
4942     getAutoCreate : function(){
4943         var cfg = {
4944             tag: 'ul',
4945                 cls: 'pagination'
4946         };
4947         if (this.inverse) {
4948             cfg.cls += ' inverse';
4949         }
4950         if (this.html) {
4951             cfg.html=this.html;
4952         }
4953         if (this.cls) {
4954             cfg.cls += " " + this.cls;
4955         }
4956         return cfg;
4957     }
4958    
4959 });
4960
4961  
4962
4963  /*
4964  * - LGPL
4965  *
4966  * Pagination item
4967  * 
4968  */
4969
4970
4971 /**
4972  * @class Roo.bootstrap.PaginationItem
4973  * @extends Roo.bootstrap.Component
4974  * Bootstrap PaginationItem class
4975  * @cfg {String} html text
4976  * @cfg {String} href the link
4977  * @cfg {Boolean} preventDefault (true | false) default true
4978  * @cfg {Boolean} active (true | false) default false
4979  * @cfg {Boolean} disabled default false
4980  * 
4981  * 
4982  * @constructor
4983  * Create a new PaginationItem
4984  * @param {Object} config The config object
4985  */
4986
4987
4988 Roo.bootstrap.PaginationItem = function(config){
4989     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4990     this.addEvents({
4991         // raw events
4992         /**
4993          * @event click
4994          * The raw click event for the entire grid.
4995          * @param {Roo.EventObject} e
4996          */
4997         "click" : true
4998     });
4999 };
5000
5001 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5002     
5003     href : false,
5004     html : false,
5005     preventDefault: true,
5006     active : false,
5007     cls : false,
5008     disabled: false,
5009     
5010     getAutoCreate : function(){
5011         var cfg= {
5012             tag: 'li',
5013             cn: [
5014                 {
5015                     tag : 'a',
5016                     href : this.href ? this.href : '#',
5017                     html : this.html ? this.html : ''
5018                 }
5019             ]
5020         };
5021         
5022         if(this.cls){
5023             cfg.cls = this.cls;
5024         }
5025         
5026         if(this.disabled){
5027             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5028         }
5029         
5030         if(this.active){
5031             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5032         }
5033         
5034         return cfg;
5035     },
5036     
5037     initEvents: function() {
5038         
5039         this.el.on('click', this.onClick, this);
5040         
5041     },
5042     onClick : function(e)
5043     {
5044         Roo.log('PaginationItem on click ');
5045         if(this.preventDefault){
5046             e.preventDefault();
5047         }
5048         
5049         if(this.disabled){
5050             return;
5051         }
5052         
5053         this.fireEvent('click', this, e);
5054     }
5055    
5056 });
5057
5058  
5059
5060  /*
5061  * - LGPL
5062  *
5063  * slider
5064  * 
5065  */
5066
5067
5068 /**
5069  * @class Roo.bootstrap.Slider
5070  * @extends Roo.bootstrap.Component
5071  * Bootstrap Slider class
5072  *    
5073  * @constructor
5074  * Create a new Slider
5075  * @param {Object} config The config object
5076  */
5077
5078 Roo.bootstrap.Slider = function(config){
5079     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5080 };
5081
5082 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5083     
5084     getAutoCreate : function(){
5085         
5086         var cfg = {
5087             tag: 'div',
5088             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5089             cn: [
5090                 {
5091                     tag: 'a',
5092                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5093                 }
5094             ]
5095         };
5096         
5097         return cfg;
5098     }
5099    
5100 });
5101
5102  /*
5103  * Based on:
5104  * Ext JS Library 1.1.1
5105  * Copyright(c) 2006-2007, Ext JS, LLC.
5106  *
5107  * Originally Released Under LGPL - original licence link has changed is not relivant.
5108  *
5109  * Fork - LGPL
5110  * <script type="text/javascript">
5111  */
5112  
5113
5114 /**
5115  * @class Roo.grid.ColumnModel
5116  * @extends Roo.util.Observable
5117  * This is the default implementation of a ColumnModel used by the Grid. It defines
5118  * the columns in the grid.
5119  * <br>Usage:<br>
5120  <pre><code>
5121  var colModel = new Roo.grid.ColumnModel([
5122         {header: "Ticker", width: 60, sortable: true, locked: true},
5123         {header: "Company Name", width: 150, sortable: true},
5124         {header: "Market Cap.", width: 100, sortable: true},
5125         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5126         {header: "Employees", width: 100, sortable: true, resizable: false}
5127  ]);
5128  </code></pre>
5129  * <p>
5130  
5131  * The config options listed for this class are options which may appear in each
5132  * individual column definition.
5133  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5134  * @constructor
5135  * @param {Object} config An Array of column config objects. See this class's
5136  * config objects for details.
5137 */
5138 Roo.grid.ColumnModel = function(config){
5139         /**
5140      * The config passed into the constructor
5141      */
5142     this.config = config;
5143     this.lookup = {};
5144
5145     // if no id, create one
5146     // if the column does not have a dataIndex mapping,
5147     // map it to the order it is in the config
5148     for(var i = 0, len = config.length; i < len; i++){
5149         var c = config[i];
5150         if(typeof c.dataIndex == "undefined"){
5151             c.dataIndex = i;
5152         }
5153         if(typeof c.renderer == "string"){
5154             c.renderer = Roo.util.Format[c.renderer];
5155         }
5156         if(typeof c.id == "undefined"){
5157             c.id = Roo.id();
5158         }
5159         if(c.editor && c.editor.xtype){
5160             c.editor  = Roo.factory(c.editor, Roo.grid);
5161         }
5162         if(c.editor && c.editor.isFormField){
5163             c.editor = new Roo.grid.GridEditor(c.editor);
5164         }
5165         this.lookup[c.id] = c;
5166     }
5167
5168     /**
5169      * The width of columns which have no width specified (defaults to 100)
5170      * @type Number
5171      */
5172     this.defaultWidth = 100;
5173
5174     /**
5175      * Default sortable of columns which have no sortable specified (defaults to false)
5176      * @type Boolean
5177      */
5178     this.defaultSortable = false;
5179
5180     this.addEvents({
5181         /**
5182              * @event widthchange
5183              * Fires when the width of a column changes.
5184              * @param {ColumnModel} this
5185              * @param {Number} columnIndex The column index
5186              * @param {Number} newWidth The new width
5187              */
5188             "widthchange": true,
5189         /**
5190              * @event headerchange
5191              * Fires when the text of a header changes.
5192              * @param {ColumnModel} this
5193              * @param {Number} columnIndex The column index
5194              * @param {Number} newText The new header text
5195              */
5196             "headerchange": true,
5197         /**
5198              * @event hiddenchange
5199              * Fires when a column is hidden or "unhidden".
5200              * @param {ColumnModel} this
5201              * @param {Number} columnIndex The column index
5202              * @param {Boolean} hidden true if hidden, false otherwise
5203              */
5204             "hiddenchange": true,
5205             /**
5206          * @event columnmoved
5207          * Fires when a column is moved.
5208          * @param {ColumnModel} this
5209          * @param {Number} oldIndex
5210          * @param {Number} newIndex
5211          */
5212         "columnmoved" : true,
5213         /**
5214          * @event columlockchange
5215          * Fires when a column's locked state is changed
5216          * @param {ColumnModel} this
5217          * @param {Number} colIndex
5218          * @param {Boolean} locked true if locked
5219          */
5220         "columnlockchange" : true
5221     });
5222     Roo.grid.ColumnModel.superclass.constructor.call(this);
5223 };
5224 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5225     /**
5226      * @cfg {String} header The header text to display in the Grid view.
5227      */
5228     /**
5229      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5230      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5231      * specified, the column's index is used as an index into the Record's data Array.
5232      */
5233     /**
5234      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5235      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5236      */
5237     /**
5238      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5239      * Defaults to the value of the {@link #defaultSortable} property.
5240      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5241      */
5242     /**
5243      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5244      */
5245     /**
5246      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5247      */
5248     /**
5249      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5250      */
5251     /**
5252      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5253      */
5254     /**
5255      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5256      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5257      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5258      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5259      */
5260        /**
5261      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5262      */
5263     /**
5264      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5265      */
5266     /**
5267      * @cfg {String} cursor (Optional)
5268      */
5269     /**
5270      * @cfg {String} tooltip (Optional)
5271      */
5272     /**
5273      * @cfg {Number} xs (Optional)
5274      */
5275     /**
5276      * @cfg {Number} sm (Optional)
5277      */
5278     /**
5279      * @cfg {Number} md (Optional)
5280      */
5281     /**
5282      * @cfg {Number} lg (Optional)
5283      */
5284     /**
5285      * Returns the id of the column at the specified index.
5286      * @param {Number} index The column index
5287      * @return {String} the id
5288      */
5289     getColumnId : function(index){
5290         return this.config[index].id;
5291     },
5292
5293     /**
5294      * Returns the column for a specified id.
5295      * @param {String} id The column id
5296      * @return {Object} the column
5297      */
5298     getColumnById : function(id){
5299         return this.lookup[id];
5300     },
5301
5302     
5303     /**
5304      * Returns the column for a specified dataIndex.
5305      * @param {String} dataIndex The column dataIndex
5306      * @return {Object|Boolean} the column or false if not found
5307      */
5308     getColumnByDataIndex: function(dataIndex){
5309         var index = this.findColumnIndex(dataIndex);
5310         return index > -1 ? this.config[index] : false;
5311     },
5312     
5313     /**
5314      * Returns the index for a specified column id.
5315      * @param {String} id The column id
5316      * @return {Number} the index, or -1 if not found
5317      */
5318     getIndexById : function(id){
5319         for(var i = 0, len = this.config.length; i < len; i++){
5320             if(this.config[i].id == id){
5321                 return i;
5322             }
5323         }
5324         return -1;
5325     },
5326     
5327     /**
5328      * Returns the index for a specified column dataIndex.
5329      * @param {String} dataIndex The column dataIndex
5330      * @return {Number} the index, or -1 if not found
5331      */
5332     
5333     findColumnIndex : function(dataIndex){
5334         for(var i = 0, len = this.config.length; i < len; i++){
5335             if(this.config[i].dataIndex == dataIndex){
5336                 return i;
5337             }
5338         }
5339         return -1;
5340     },
5341     
5342     
5343     moveColumn : function(oldIndex, newIndex){
5344         var c = this.config[oldIndex];
5345         this.config.splice(oldIndex, 1);
5346         this.config.splice(newIndex, 0, c);
5347         this.dataMap = null;
5348         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5349     },
5350
5351     isLocked : function(colIndex){
5352         return this.config[colIndex].locked === true;
5353     },
5354
5355     setLocked : function(colIndex, value, suppressEvent){
5356         if(this.isLocked(colIndex) == value){
5357             return;
5358         }
5359         this.config[colIndex].locked = value;
5360         if(!suppressEvent){
5361             this.fireEvent("columnlockchange", this, colIndex, value);
5362         }
5363     },
5364
5365     getTotalLockedWidth : function(){
5366         var totalWidth = 0;
5367         for(var i = 0; i < this.config.length; i++){
5368             if(this.isLocked(i) && !this.isHidden(i)){
5369                 this.totalWidth += this.getColumnWidth(i);
5370             }
5371         }
5372         return totalWidth;
5373     },
5374
5375     getLockedCount : function(){
5376         for(var i = 0, len = this.config.length; i < len; i++){
5377             if(!this.isLocked(i)){
5378                 return i;
5379             }
5380         }
5381         
5382         return this.config.length;
5383     },
5384
5385     /**
5386      * Returns the number of columns.
5387      * @return {Number}
5388      */
5389     getColumnCount : function(visibleOnly){
5390         if(visibleOnly === true){
5391             var c = 0;
5392             for(var i = 0, len = this.config.length; i < len; i++){
5393                 if(!this.isHidden(i)){
5394                     c++;
5395                 }
5396             }
5397             return c;
5398         }
5399         return this.config.length;
5400     },
5401
5402     /**
5403      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5404      * @param {Function} fn
5405      * @param {Object} scope (optional)
5406      * @return {Array} result
5407      */
5408     getColumnsBy : function(fn, scope){
5409         var r = [];
5410         for(var i = 0, len = this.config.length; i < len; i++){
5411             var c = this.config[i];
5412             if(fn.call(scope||this, c, i) === true){
5413                 r[r.length] = c;
5414             }
5415         }
5416         return r;
5417     },
5418
5419     /**
5420      * Returns true if the specified column is sortable.
5421      * @param {Number} col The column index
5422      * @return {Boolean}
5423      */
5424     isSortable : function(col){
5425         if(typeof this.config[col].sortable == "undefined"){
5426             return this.defaultSortable;
5427         }
5428         return this.config[col].sortable;
5429     },
5430
5431     /**
5432      * Returns the rendering (formatting) function defined for the column.
5433      * @param {Number} col The column index.
5434      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5435      */
5436     getRenderer : function(col){
5437         if(!this.config[col].renderer){
5438             return Roo.grid.ColumnModel.defaultRenderer;
5439         }
5440         return this.config[col].renderer;
5441     },
5442
5443     /**
5444      * Sets the rendering (formatting) function for a column.
5445      * @param {Number} col The column index
5446      * @param {Function} fn The function to use to process the cell's raw data
5447      * to return HTML markup for the grid view. The render function is called with
5448      * the following parameters:<ul>
5449      * <li>Data value.</li>
5450      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5451      * <li>css A CSS style string to apply to the table cell.</li>
5452      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5453      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5454      * <li>Row index</li>
5455      * <li>Column index</li>
5456      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5457      */
5458     setRenderer : function(col, fn){
5459         this.config[col].renderer = fn;
5460     },
5461
5462     /**
5463      * Returns the width for the specified column.
5464      * @param {Number} col The column index
5465      * @return {Number}
5466      */
5467     getColumnWidth : function(col){
5468         return this.config[col].width * 1 || this.defaultWidth;
5469     },
5470
5471     /**
5472      * Sets the width for a column.
5473      * @param {Number} col The column index
5474      * @param {Number} width The new width
5475      */
5476     setColumnWidth : function(col, width, suppressEvent){
5477         this.config[col].width = width;
5478         this.totalWidth = null;
5479         if(!suppressEvent){
5480              this.fireEvent("widthchange", this, col, width);
5481         }
5482     },
5483
5484     /**
5485      * Returns the total width of all columns.
5486      * @param {Boolean} includeHidden True to include hidden column widths
5487      * @return {Number}
5488      */
5489     getTotalWidth : function(includeHidden){
5490         if(!this.totalWidth){
5491             this.totalWidth = 0;
5492             for(var i = 0, len = this.config.length; i < len; i++){
5493                 if(includeHidden || !this.isHidden(i)){
5494                     this.totalWidth += this.getColumnWidth(i);
5495                 }
5496             }
5497         }
5498         return this.totalWidth;
5499     },
5500
5501     /**
5502      * Returns the header for the specified column.
5503      * @param {Number} col The column index
5504      * @return {String}
5505      */
5506     getColumnHeader : function(col){
5507         return this.config[col].header;
5508     },
5509
5510     /**
5511      * Sets the header for a column.
5512      * @param {Number} col The column index
5513      * @param {String} header The new header
5514      */
5515     setColumnHeader : function(col, header){
5516         this.config[col].header = header;
5517         this.fireEvent("headerchange", this, col, header);
5518     },
5519
5520     /**
5521      * Returns the tooltip for the specified column.
5522      * @param {Number} col The column index
5523      * @return {String}
5524      */
5525     getColumnTooltip : function(col){
5526             return this.config[col].tooltip;
5527     },
5528     /**
5529      * Sets the tooltip for a column.
5530      * @param {Number} col The column index
5531      * @param {String} tooltip The new tooltip
5532      */
5533     setColumnTooltip : function(col, tooltip){
5534             this.config[col].tooltip = tooltip;
5535     },
5536
5537     /**
5538      * Returns the dataIndex for the specified column.
5539      * @param {Number} col The column index
5540      * @return {Number}
5541      */
5542     getDataIndex : function(col){
5543         return this.config[col].dataIndex;
5544     },
5545
5546     /**
5547      * Sets the dataIndex for a column.
5548      * @param {Number} col The column index
5549      * @param {Number} dataIndex The new dataIndex
5550      */
5551     setDataIndex : function(col, dataIndex){
5552         this.config[col].dataIndex = dataIndex;
5553     },
5554
5555     
5556     
5557     /**
5558      * Returns true if the cell is editable.
5559      * @param {Number} colIndex The column index
5560      * @param {Number} rowIndex The row index - this is nto actually used..?
5561      * @return {Boolean}
5562      */
5563     isCellEditable : function(colIndex, rowIndex){
5564         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5565     },
5566
5567     /**
5568      * Returns the editor defined for the cell/column.
5569      * return false or null to disable editing.
5570      * @param {Number} colIndex The column index
5571      * @param {Number} rowIndex The row index
5572      * @return {Object}
5573      */
5574     getCellEditor : function(colIndex, rowIndex){
5575         return this.config[colIndex].editor;
5576     },
5577
5578     /**
5579      * Sets if a column is editable.
5580      * @param {Number} col The column index
5581      * @param {Boolean} editable True if the column is editable
5582      */
5583     setEditable : function(col, editable){
5584         this.config[col].editable = editable;
5585     },
5586
5587
5588     /**
5589      * Returns true if the column is hidden.
5590      * @param {Number} colIndex The column index
5591      * @return {Boolean}
5592      */
5593     isHidden : function(colIndex){
5594         return this.config[colIndex].hidden;
5595     },
5596
5597
5598     /**
5599      * Returns true if the column width cannot be changed
5600      */
5601     isFixed : function(colIndex){
5602         return this.config[colIndex].fixed;
5603     },
5604
5605     /**
5606      * Returns true if the column can be resized
5607      * @return {Boolean}
5608      */
5609     isResizable : function(colIndex){
5610         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5611     },
5612     /**
5613      * Sets if a column is hidden.
5614      * @param {Number} colIndex The column index
5615      * @param {Boolean} hidden True if the column is hidden
5616      */
5617     setHidden : function(colIndex, hidden){
5618         this.config[colIndex].hidden = hidden;
5619         this.totalWidth = null;
5620         this.fireEvent("hiddenchange", this, colIndex, hidden);
5621     },
5622
5623     /**
5624      * Sets the editor for a column.
5625      * @param {Number} col The column index
5626      * @param {Object} editor The editor object
5627      */
5628     setEditor : function(col, editor){
5629         this.config[col].editor = editor;
5630     }
5631 });
5632
5633 Roo.grid.ColumnModel.defaultRenderer = function(value)
5634 {
5635     if(typeof value == "object") {
5636         return value;
5637     }
5638         if(typeof value == "string" && value.length < 1){
5639             return "&#160;";
5640         }
5641     
5642         return String.format("{0}", value);
5643 };
5644
5645 // Alias for backwards compatibility
5646 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5647 /*
5648  * Based on:
5649  * Ext JS Library 1.1.1
5650  * Copyright(c) 2006-2007, Ext JS, LLC.
5651  *
5652  * Originally Released Under LGPL - original licence link has changed is not relivant.
5653  *
5654  * Fork - LGPL
5655  * <script type="text/javascript">
5656  */
5657  
5658 /**
5659  * @class Roo.LoadMask
5660  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5661  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5662  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5663  * element's UpdateManager load indicator and will be destroyed after the initial load.
5664  * @constructor
5665  * Create a new LoadMask
5666  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5667  * @param {Object} config The config object
5668  */
5669 Roo.LoadMask = function(el, config){
5670     this.el = Roo.get(el);
5671     Roo.apply(this, config);
5672     if(this.store){
5673         this.store.on('beforeload', this.onBeforeLoad, this);
5674         this.store.on('load', this.onLoad, this);
5675         this.store.on('loadexception', this.onLoadException, this);
5676         this.removeMask = false;
5677     }else{
5678         var um = this.el.getUpdateManager();
5679         um.showLoadIndicator = false; // disable the default indicator
5680         um.on('beforeupdate', this.onBeforeLoad, this);
5681         um.on('update', this.onLoad, this);
5682         um.on('failure', this.onLoad, this);
5683         this.removeMask = true;
5684     }
5685 };
5686
5687 Roo.LoadMask.prototype = {
5688     /**
5689      * @cfg {Boolean} removeMask
5690      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5691      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5692      */
5693     /**
5694      * @cfg {String} msg
5695      * The text to display in a centered loading message box (defaults to 'Loading...')
5696      */
5697     msg : 'Loading...',
5698     /**
5699      * @cfg {String} msgCls
5700      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5701      */
5702     msgCls : 'x-mask-loading',
5703
5704     /**
5705      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5706      * @type Boolean
5707      */
5708     disabled: false,
5709
5710     /**
5711      * Disables the mask to prevent it from being displayed
5712      */
5713     disable : function(){
5714        this.disabled = true;
5715     },
5716
5717     /**
5718      * Enables the mask so that it can be displayed
5719      */
5720     enable : function(){
5721         this.disabled = false;
5722     },
5723     
5724     onLoadException : function()
5725     {
5726         Roo.log(arguments);
5727         
5728         if (typeof(arguments[3]) != 'undefined') {
5729             Roo.MessageBox.alert("Error loading",arguments[3]);
5730         } 
5731         /*
5732         try {
5733             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5734                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5735             }   
5736         } catch(e) {
5737             
5738         }
5739         */
5740     
5741         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5742     },
5743     // private
5744     onLoad : function()
5745     {
5746         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5747     },
5748
5749     // private
5750     onBeforeLoad : function(){
5751         if(!this.disabled){
5752             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5753         }
5754     },
5755
5756     // private
5757     destroy : function(){
5758         if(this.store){
5759             this.store.un('beforeload', this.onBeforeLoad, this);
5760             this.store.un('load', this.onLoad, this);
5761             this.store.un('loadexception', this.onLoadException, this);
5762         }else{
5763             var um = this.el.getUpdateManager();
5764             um.un('beforeupdate', this.onBeforeLoad, this);
5765             um.un('update', this.onLoad, this);
5766             um.un('failure', this.onLoad, this);
5767         }
5768     }
5769 };/*
5770  * - LGPL
5771  *
5772  * table
5773  * 
5774  */
5775
5776 /**
5777  * @class Roo.bootstrap.Table
5778  * @extends Roo.bootstrap.Component
5779  * Bootstrap Table class
5780  * @cfg {String} cls table class
5781  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5782  * @cfg {String} bgcolor Specifies the background color for a table
5783  * @cfg {Number} border Specifies whether the table cells should have borders or not
5784  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5785  * @cfg {Number} cellspacing Specifies the space between cells
5786  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5787  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5788  * @cfg {String} sortable Specifies that the table should be sortable
5789  * @cfg {String} summary Specifies a summary of the content of a table
5790  * @cfg {Number} width Specifies the width of a table
5791  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5792  * 
5793  * @cfg {boolean} striped Should the rows be alternative striped
5794  * @cfg {boolean} bordered Add borders to the table
5795  * @cfg {boolean} hover Add hover highlighting
5796  * @cfg {boolean} condensed Format condensed
5797  * @cfg {boolean} responsive Format condensed
5798  * @cfg {Boolean} loadMask (true|false) default false
5799  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5800  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5801  * @cfg {Boolean} rowSelection (true|false) default false
5802  * @cfg {Boolean} cellSelection (true|false) default false
5803  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5804  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5805  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5806  
5807  * 
5808  * @constructor
5809  * Create a new Table
5810  * @param {Object} config The config object
5811  */
5812
5813 Roo.bootstrap.Table = function(config){
5814     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5815     
5816   
5817     
5818     // BC...
5819     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5820     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5821     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5822     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5823     
5824     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5825     if (this.sm) {
5826         this.sm.grid = this;
5827         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5828         this.sm = this.selModel;
5829         this.sm.xmodule = this.xmodule || false;
5830     }
5831     
5832     if (this.cm && typeof(this.cm.config) == 'undefined') {
5833         this.colModel = new Roo.grid.ColumnModel(this.cm);
5834         this.cm = this.colModel;
5835         this.cm.xmodule = this.xmodule || false;
5836     }
5837     if (this.store) {
5838         this.store= Roo.factory(this.store, Roo.data);
5839         this.ds = this.store;
5840         this.ds.xmodule = this.xmodule || false;
5841          
5842     }
5843     if (this.footer && this.store) {
5844         this.footer.dataSource = this.ds;
5845         this.footer = Roo.factory(this.footer);
5846     }
5847     
5848     /** @private */
5849     this.addEvents({
5850         /**
5851          * @event cellclick
5852          * Fires when a cell is clicked
5853          * @param {Roo.bootstrap.Table} this
5854          * @param {Roo.Element} el
5855          * @param {Number} rowIndex
5856          * @param {Number} columnIndex
5857          * @param {Roo.EventObject} e
5858          */
5859         "cellclick" : true,
5860         /**
5861          * @event celldblclick
5862          * Fires when a cell is double clicked
5863          * @param {Roo.bootstrap.Table} this
5864          * @param {Roo.Element} el
5865          * @param {Number} rowIndex
5866          * @param {Number} columnIndex
5867          * @param {Roo.EventObject} e
5868          */
5869         "celldblclick" : true,
5870         /**
5871          * @event rowclick
5872          * Fires when a row is clicked
5873          * @param {Roo.bootstrap.Table} this
5874          * @param {Roo.Element} el
5875          * @param {Number} rowIndex
5876          * @param {Roo.EventObject} e
5877          */
5878         "rowclick" : true,
5879         /**
5880          * @event rowdblclick
5881          * Fires when a row is double clicked
5882          * @param {Roo.bootstrap.Table} this
5883          * @param {Roo.Element} el
5884          * @param {Number} rowIndex
5885          * @param {Roo.EventObject} e
5886          */
5887         "rowdblclick" : true,
5888         /**
5889          * @event mouseover
5890          * Fires when a mouseover occur
5891          * @param {Roo.bootstrap.Table} this
5892          * @param {Roo.Element} el
5893          * @param {Number} rowIndex
5894          * @param {Number} columnIndex
5895          * @param {Roo.EventObject} e
5896          */
5897         "mouseover" : true,
5898         /**
5899          * @event mouseout
5900          * Fires when a mouseout occur
5901          * @param {Roo.bootstrap.Table} this
5902          * @param {Roo.Element} el
5903          * @param {Number} rowIndex
5904          * @param {Number} columnIndex
5905          * @param {Roo.EventObject} e
5906          */
5907         "mouseout" : true,
5908         /**
5909          * @event rowclass
5910          * Fires when a row is rendered, so you can change add a style to it.
5911          * @param {Roo.bootstrap.Table} this
5912          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5913          */
5914         'rowclass' : true,
5915           /**
5916          * @event rowsrendered
5917          * Fires when all the  rows have been rendered
5918          * @param {Roo.bootstrap.Table} this
5919          */
5920         'rowsrendered' : true,
5921         /**
5922          * @event contextmenu
5923          * The raw contextmenu event for the entire grid.
5924          * @param {Roo.EventObject} e
5925          */
5926         "contextmenu" : true,
5927         /**
5928          * @event rowcontextmenu
5929          * Fires when a row is right clicked
5930          * @param {Roo.bootstrap.Table} this
5931          * @param {Number} rowIndex
5932          * @param {Roo.EventObject} e
5933          */
5934         "rowcontextmenu" : true,
5935         /**
5936          * @event cellcontextmenu
5937          * Fires when a cell is right clicked
5938          * @param {Roo.bootstrap.Table} this
5939          * @param {Number} rowIndex
5940          * @param {Number} cellIndex
5941          * @param {Roo.EventObject} e
5942          */
5943          "cellcontextmenu" : true,
5944          /**
5945          * @event headercontextmenu
5946          * Fires when a header is right clicked
5947          * @param {Roo.bootstrap.Table} this
5948          * @param {Number} columnIndex
5949          * @param {Roo.EventObject} e
5950          */
5951         "headercontextmenu" : true
5952     });
5953 };
5954
5955 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5956     
5957     cls: false,
5958     align: false,
5959     bgcolor: false,
5960     border: false,
5961     cellpadding: false,
5962     cellspacing: false,
5963     frame: false,
5964     rules: false,
5965     sortable: false,
5966     summary: false,
5967     width: false,
5968     striped : false,
5969     scrollBody : false,
5970     bordered: false,
5971     hover:  false,
5972     condensed : false,
5973     responsive : false,
5974     sm : false,
5975     cm : false,
5976     store : false,
5977     loadMask : false,
5978     footerShow : true,
5979     headerShow : true,
5980   
5981     rowSelection : false,
5982     cellSelection : false,
5983     layout : false,
5984     
5985     // Roo.Element - the tbody
5986     mainBody: false,
5987     // Roo.Element - thead element
5988     mainHead: false,
5989     
5990     container: false, // used by gridpanel...
5991     
5992     lazyLoad : false,
5993     
5994     getAutoCreate : function()
5995     {
5996         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5997         
5998         cfg = {
5999             tag: 'table',
6000             cls : 'table',
6001             cn : []
6002         };
6003         if (this.scrollBody) {
6004             cfg.cls += ' table-body-fixed';
6005         }    
6006         if (this.striped) {
6007             cfg.cls += ' table-striped';
6008         }
6009         
6010         if (this.hover) {
6011             cfg.cls += ' table-hover';
6012         }
6013         if (this.bordered) {
6014             cfg.cls += ' table-bordered';
6015         }
6016         if (this.condensed) {
6017             cfg.cls += ' table-condensed';
6018         }
6019         if (this.responsive) {
6020             cfg.cls += ' table-responsive';
6021         }
6022         
6023         if (this.cls) {
6024             cfg.cls+=  ' ' +this.cls;
6025         }
6026         
6027         // this lot should be simplifed...
6028         
6029         if (this.align) {
6030             cfg.align=this.align;
6031         }
6032         if (this.bgcolor) {
6033             cfg.bgcolor=this.bgcolor;
6034         }
6035         if (this.border) {
6036             cfg.border=this.border;
6037         }
6038         if (this.cellpadding) {
6039             cfg.cellpadding=this.cellpadding;
6040         }
6041         if (this.cellspacing) {
6042             cfg.cellspacing=this.cellspacing;
6043         }
6044         if (this.frame) {
6045             cfg.frame=this.frame;
6046         }
6047         if (this.rules) {
6048             cfg.rules=this.rules;
6049         }
6050         if (this.sortable) {
6051             cfg.sortable=this.sortable;
6052         }
6053         if (this.summary) {
6054             cfg.summary=this.summary;
6055         }
6056         if (this.width) {
6057             cfg.width=this.width;
6058         }
6059         if (this.layout) {
6060             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6061         }
6062         
6063         if(this.store || this.cm){
6064             if(this.headerShow){
6065                 cfg.cn.push(this.renderHeader());
6066             }
6067             
6068             cfg.cn.push(this.renderBody());
6069             
6070             if(this.footerShow){
6071                 cfg.cn.push(this.renderFooter());
6072             }
6073             // where does this come from?
6074             //cfg.cls+=  ' TableGrid';
6075         }
6076         
6077         return { cn : [ cfg ] };
6078     },
6079     
6080     initEvents : function()
6081     {   
6082         if(!this.store || !this.cm){
6083             return;
6084         }
6085         if (this.selModel) {
6086             this.selModel.initEvents();
6087         }
6088         
6089         
6090         //Roo.log('initEvents with ds!!!!');
6091         
6092         this.mainBody = this.el.select('tbody', true).first();
6093         this.mainHead = this.el.select('thead', true).first();
6094         
6095         
6096         
6097         
6098         var _this = this;
6099         
6100         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6101             e.on('click', _this.sort, _this);
6102         });
6103         
6104         this.mainBody.on("click", this.onClick, this);
6105         this.mainBody.on("dblclick", this.onDblClick, this);
6106         
6107         // why is this done????? = it breaks dialogs??
6108         //this.parent().el.setStyle('position', 'relative');
6109         
6110         
6111         if (this.footer) {
6112             this.footer.parentId = this.id;
6113             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6114             
6115             if(this.lazyLoad){
6116                 this.el.select('tfoot tr td').first().addClass('hide');
6117             }
6118         } 
6119         
6120         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6121         
6122         this.store.on('load', this.onLoad, this);
6123         this.store.on('beforeload', this.onBeforeLoad, this);
6124         this.store.on('update', this.onUpdate, this);
6125         this.store.on('add', this.onAdd, this);
6126         this.store.on("clear", this.clear, this);
6127         
6128         this.el.on("contextmenu", this.onContextMenu, this);
6129         
6130         this.mainBody.on('scroll', this.onBodyScroll, this);
6131         
6132         
6133     },
6134     
6135     onContextMenu : function(e, t)
6136     {
6137         this.processEvent("contextmenu", e);
6138     },
6139     
6140     processEvent : function(name, e)
6141     {
6142         if (name != 'touchstart' ) {
6143             this.fireEvent(name, e);    
6144         }
6145         
6146         var t = e.getTarget();
6147         
6148         var cell = Roo.get(t);
6149         
6150         if(!cell){
6151             return;
6152         }
6153         
6154         if(cell.findParent('tfoot', false, true)){
6155             return;
6156         }
6157         
6158         if(cell.findParent('thead', false, true)){
6159             
6160             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6161                 cell = Roo.get(t).findParent('th', false, true);
6162                 if (!cell) {
6163                     Roo.log("failed to find th in thead?");
6164                     Roo.log(e.getTarget());
6165                     return;
6166                 }
6167             }
6168             
6169             var cellIndex = cell.dom.cellIndex;
6170             
6171             var ename = name == 'touchstart' ? 'click' : name;
6172             this.fireEvent("header" + ename, this, cellIndex, e);
6173             
6174             return;
6175         }
6176         
6177         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6178             cell = Roo.get(t).findParent('td', false, true);
6179             if (!cell) {
6180                 Roo.log("failed to find th in tbody?");
6181                 Roo.log(e.getTarget());
6182                 return;
6183             }
6184         }
6185         
6186         var row = cell.findParent('tr', false, true);
6187         var cellIndex = cell.dom.cellIndex;
6188         var rowIndex = row.dom.rowIndex - 1;
6189         
6190         if(row !== false){
6191             
6192             this.fireEvent("row" + name, this, rowIndex, e);
6193             
6194             if(cell !== false){
6195             
6196                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6197             }
6198         }
6199         
6200     },
6201     
6202     onMouseover : function(e, el)
6203     {
6204         var cell = Roo.get(el);
6205         
6206         if(!cell){
6207             return;
6208         }
6209         
6210         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6211             cell = cell.findParent('td', false, true);
6212         }
6213         
6214         var row = cell.findParent('tr', false, true);
6215         var cellIndex = cell.dom.cellIndex;
6216         var rowIndex = row.dom.rowIndex - 1; // start from 0
6217         
6218         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6219         
6220     },
6221     
6222     onMouseout : function(e, el)
6223     {
6224         var cell = Roo.get(el);
6225         
6226         if(!cell){
6227             return;
6228         }
6229         
6230         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6231             cell = cell.findParent('td', false, true);
6232         }
6233         
6234         var row = cell.findParent('tr', false, true);
6235         var cellIndex = cell.dom.cellIndex;
6236         var rowIndex = row.dom.rowIndex - 1; // start from 0
6237         
6238         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6239         
6240     },
6241     
6242     onClick : function(e, el)
6243     {
6244         var cell = Roo.get(el);
6245         
6246         if(!cell || (!this.cellSelection && !this.rowSelection)){
6247             return;
6248         }
6249         
6250         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6251             cell = cell.findParent('td', false, true);
6252         }
6253         
6254         if(!cell || typeof(cell) == 'undefined'){
6255             return;
6256         }
6257         
6258         var row = cell.findParent('tr', false, true);
6259         
6260         if(!row || typeof(row) == 'undefined'){
6261             return;
6262         }
6263         
6264         var cellIndex = cell.dom.cellIndex;
6265         var rowIndex = this.getRowIndex(row);
6266         
6267         // why??? - should these not be based on SelectionModel?
6268         if(this.cellSelection){
6269             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6270         }
6271         
6272         if(this.rowSelection){
6273             this.fireEvent('rowclick', this, row, rowIndex, e);
6274         }
6275         
6276         
6277     },
6278         
6279     onDblClick : function(e,el)
6280     {
6281         var cell = Roo.get(el);
6282         
6283         if(!cell || (!this.cellSelection && !this.rowSelection)){
6284             return;
6285         }
6286         
6287         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6288             cell = cell.findParent('td', false, true);
6289         }
6290         
6291         if(!cell || typeof(cell) == 'undefined'){
6292             return;
6293         }
6294         
6295         var row = cell.findParent('tr', false, true);
6296         
6297         if(!row || typeof(row) == 'undefined'){
6298             return;
6299         }
6300         
6301         var cellIndex = cell.dom.cellIndex;
6302         var rowIndex = this.getRowIndex(row);
6303         
6304         if(this.cellSelection){
6305             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6306         }
6307         
6308         if(this.rowSelection){
6309             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6310         }
6311     },
6312     
6313     sort : function(e,el)
6314     {
6315         var col = Roo.get(el);
6316         
6317         if(!col.hasClass('sortable')){
6318             return;
6319         }
6320         
6321         var sort = col.attr('sort');
6322         var dir = 'ASC';
6323         
6324         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6325             dir = 'DESC';
6326         }
6327         
6328         this.store.sortInfo = {field : sort, direction : dir};
6329         
6330         if (this.footer) {
6331             Roo.log("calling footer first");
6332             this.footer.onClick('first');
6333         } else {
6334         
6335             this.store.load({ params : { start : 0 } });
6336         }
6337     },
6338     
6339     renderHeader : function()
6340     {
6341         var header = {
6342             tag: 'thead',
6343             cn : []
6344         };
6345         
6346         var cm = this.cm;
6347         this.totalWidth = 0;
6348         
6349         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6350             
6351             var config = cm.config[i];
6352             
6353             var c = {
6354                 tag: 'th',
6355                 style : '',
6356                 html: cm.getColumnHeader(i)
6357             };
6358             
6359             var hh = '';
6360             
6361             if(typeof(config.sortable) != 'undefined' && config.sortable){
6362                 c.cls = 'sortable';
6363                 c.html = '<i class="glyphicon"></i>' + c.html;
6364             }
6365             
6366             if(typeof(config.lgHeader) != 'undefined'){
6367                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6368             }
6369             
6370             if(typeof(config.mdHeader) != 'undefined'){
6371                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6372             }
6373             
6374             if(typeof(config.smHeader) != 'undefined'){
6375                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6376             }
6377             
6378             if(typeof(config.xsHeader) != 'undefined'){
6379                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6380             }
6381             
6382             if(hh.length){
6383                 c.html = hh;
6384             }
6385             
6386             if(typeof(config.tooltip) != 'undefined'){
6387                 c.tooltip = config.tooltip;
6388             }
6389             
6390             if(typeof(config.colspan) != 'undefined'){
6391                 c.colspan = config.colspan;
6392             }
6393             
6394             if(typeof(config.hidden) != 'undefined' && config.hidden){
6395                 c.style += ' display:none;';
6396             }
6397             
6398             if(typeof(config.dataIndex) != 'undefined'){
6399                 c.sort = config.dataIndex;
6400             }
6401             
6402            
6403             
6404             if(typeof(config.align) != 'undefined' && config.align.length){
6405                 c.style += ' text-align:' + config.align + ';';
6406             }
6407             
6408             if(typeof(config.width) != 'undefined'){
6409                 c.style += ' width:' + config.width + 'px;';
6410                 this.totalWidth += config.width;
6411             } else {
6412                 this.totalWidth += 100; // assume minimum of 100 per column?
6413             }
6414             
6415             if(typeof(config.cls) != 'undefined'){
6416                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6417             }
6418             
6419             ['xs','sm','md','lg'].map(function(size){
6420                 
6421                 if(typeof(config[size]) == 'undefined'){
6422                     return;
6423                 }
6424                 
6425                 if (!config[size]) { // 0 = hidden
6426                     c.cls += ' hidden-' + size;
6427                     return;
6428                 }
6429                 
6430                 c.cls += ' col-' + size + '-' + config[size];
6431
6432             });
6433             
6434             header.cn.push(c)
6435         }
6436         
6437         return header;
6438     },
6439     
6440     renderBody : function()
6441     {
6442         var body = {
6443             tag: 'tbody',
6444             cn : [
6445                 {
6446                     tag: 'tr',
6447                     cn : [
6448                         {
6449                             tag : 'td',
6450                             colspan :  this.cm.getColumnCount()
6451                         }
6452                     ]
6453                 }
6454             ]
6455         };
6456         
6457         return body;
6458     },
6459     
6460     renderFooter : function()
6461     {
6462         var footer = {
6463             tag: 'tfoot',
6464             cn : [
6465                 {
6466                     tag: 'tr',
6467                     cn : [
6468                         {
6469                             tag : 'td',
6470                             colspan :  this.cm.getColumnCount()
6471                         }
6472                     ]
6473                 }
6474             ]
6475         };
6476         
6477         return footer;
6478     },
6479     
6480     
6481     
6482     onLoad : function()
6483     {
6484 //        Roo.log('ds onload');
6485         this.clear();
6486         
6487         var _this = this;
6488         var cm = this.cm;
6489         var ds = this.store;
6490         
6491         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6492             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6493             if (_this.store.sortInfo) {
6494                     
6495                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6496                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6497                 }
6498                 
6499                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6500                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6501                 }
6502             }
6503         });
6504         
6505         var tbody =  this.mainBody;
6506               
6507         if(ds.getCount() > 0){
6508             ds.data.each(function(d,rowIndex){
6509                 var row =  this.renderRow(cm, ds, rowIndex);
6510                 
6511                 tbody.createChild(row);
6512                 
6513                 var _this = this;
6514                 
6515                 if(row.cellObjects.length){
6516                     Roo.each(row.cellObjects, function(r){
6517                         _this.renderCellObject(r);
6518                     })
6519                 }
6520                 
6521             }, this);
6522         }
6523         
6524         Roo.each(this.el.select('tbody td', true).elements, function(e){
6525             e.on('mouseover', _this.onMouseover, _this);
6526         });
6527         
6528         Roo.each(this.el.select('tbody td', true).elements, function(e){
6529             e.on('mouseout', _this.onMouseout, _this);
6530         });
6531         this.fireEvent('rowsrendered', this);
6532         //if(this.loadMask){
6533         //    this.maskEl.hide();
6534         //}
6535         
6536         this.autoSize();
6537     },
6538     
6539     
6540     onUpdate : function(ds,record)
6541     {
6542         this.refreshRow(record);
6543         this.autoSize();
6544     },
6545     
6546     onRemove : function(ds, record, index, isUpdate){
6547         if(isUpdate !== true){
6548             this.fireEvent("beforerowremoved", this, index, record);
6549         }
6550         var bt = this.mainBody.dom;
6551         
6552         var rows = this.el.select('tbody > tr', true).elements;
6553         
6554         if(typeof(rows[index]) != 'undefined'){
6555             bt.removeChild(rows[index].dom);
6556         }
6557         
6558 //        if(bt.rows[index]){
6559 //            bt.removeChild(bt.rows[index]);
6560 //        }
6561         
6562         if(isUpdate !== true){
6563             //this.stripeRows(index);
6564             //this.syncRowHeights(index, index);
6565             //this.layout();
6566             this.fireEvent("rowremoved", this, index, record);
6567         }
6568     },
6569     
6570     onAdd : function(ds, records, rowIndex)
6571     {
6572         //Roo.log('on Add called');
6573         // - note this does not handle multiple adding very well..
6574         var bt = this.mainBody.dom;
6575         for (var i =0 ; i < records.length;i++) {
6576             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6577             //Roo.log(records[i]);
6578             //Roo.log(this.store.getAt(rowIndex+i));
6579             this.insertRow(this.store, rowIndex + i, false);
6580             return;
6581         }
6582         
6583     },
6584     
6585     
6586     refreshRow : function(record){
6587         var ds = this.store, index;
6588         if(typeof record == 'number'){
6589             index = record;
6590             record = ds.getAt(index);
6591         }else{
6592             index = ds.indexOf(record);
6593         }
6594         this.insertRow(ds, index, true);
6595         this.autoSize();
6596         this.onRemove(ds, record, index+1, true);
6597         this.autoSize();
6598         //this.syncRowHeights(index, index);
6599         //this.layout();
6600         this.fireEvent("rowupdated", this, index, record);
6601     },
6602     
6603     insertRow : function(dm, rowIndex, isUpdate){
6604         
6605         if(!isUpdate){
6606             this.fireEvent("beforerowsinserted", this, rowIndex);
6607         }
6608             //var s = this.getScrollState();
6609         var row = this.renderRow(this.cm, this.store, rowIndex);
6610         // insert before rowIndex..
6611         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6612         
6613         var _this = this;
6614                 
6615         if(row.cellObjects.length){
6616             Roo.each(row.cellObjects, function(r){
6617                 _this.renderCellObject(r);
6618             })
6619         }
6620             
6621         if(!isUpdate){
6622             this.fireEvent("rowsinserted", this, rowIndex);
6623             //this.syncRowHeights(firstRow, lastRow);
6624             //this.stripeRows(firstRow);
6625             //this.layout();
6626         }
6627         
6628     },
6629     
6630     
6631     getRowDom : function(rowIndex)
6632     {
6633         var rows = this.el.select('tbody > tr', true).elements;
6634         
6635         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6636         
6637     },
6638     // returns the object tree for a tr..
6639   
6640     
6641     renderRow : function(cm, ds, rowIndex) 
6642     {
6643         
6644         var d = ds.getAt(rowIndex);
6645         
6646         var row = {
6647             tag : 'tr',
6648             cn : []
6649         };
6650             
6651         var cellObjects = [];
6652         
6653         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6654             var config = cm.config[i];
6655             
6656             var renderer = cm.getRenderer(i);
6657             var value = '';
6658             var id = false;
6659             
6660             if(typeof(renderer) !== 'undefined'){
6661                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6662             }
6663             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6664             // and are rendered into the cells after the row is rendered - using the id for the element.
6665             
6666             if(typeof(value) === 'object'){
6667                 id = Roo.id();
6668                 cellObjects.push({
6669                     container : id,
6670                     cfg : value 
6671                 })
6672             }
6673             
6674             var rowcfg = {
6675                 record: d,
6676                 rowIndex : rowIndex,
6677                 colIndex : i,
6678                 rowClass : ''
6679             };
6680
6681             this.fireEvent('rowclass', this, rowcfg);
6682             
6683             var td = {
6684                 tag: 'td',
6685                 cls : rowcfg.rowClass,
6686                 style: '',
6687                 html: (typeof(value) === 'object') ? '' : value
6688             };
6689             
6690             if (id) {
6691                 td.id = id;
6692             }
6693             
6694             if(typeof(config.colspan) != 'undefined'){
6695                 td.colspan = config.colspan;
6696             }
6697             
6698             if(typeof(config.hidden) != 'undefined' && config.hidden){
6699                 td.style += ' display:none;';
6700             }
6701             
6702             if(typeof(config.align) != 'undefined' && config.align.length){
6703                 td.style += ' text-align:' + config.align + ';';
6704             }
6705             
6706             if(typeof(config.width) != 'undefined'){
6707                 td.style += ' width:' +  config.width + 'px;';
6708             }
6709             
6710             if(typeof(config.cursor) != 'undefined'){
6711                 td.style += ' cursor:' +  config.cursor + ';';
6712             }
6713             
6714             if(typeof(config.cls) != 'undefined'){
6715                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6716             }
6717             
6718             ['xs','sm','md','lg'].map(function(size){
6719                 
6720                 if(typeof(config[size]) == 'undefined'){
6721                     return;
6722                 }
6723                 
6724                 if (!config[size]) { // 0 = hidden
6725                     td.cls += ' hidden-' + size;
6726                     return;
6727                 }
6728                 
6729                 td.cls += ' col-' + size + '-' + config[size];
6730
6731             });
6732              
6733             row.cn.push(td);
6734            
6735         }
6736         
6737         row.cellObjects = cellObjects;
6738         
6739         return row;
6740           
6741     },
6742     
6743     
6744     
6745     onBeforeLoad : function()
6746     {
6747         //Roo.log('ds onBeforeLoad');
6748         
6749         //this.clear();
6750         
6751         //if(this.loadMask){
6752         //    this.maskEl.show();
6753         //}
6754     },
6755      /**
6756      * Remove all rows
6757      */
6758     clear : function()
6759     {
6760         this.el.select('tbody', true).first().dom.innerHTML = '';
6761     },
6762     /**
6763      * Show or hide a row.
6764      * @param {Number} rowIndex to show or hide
6765      * @param {Boolean} state hide
6766      */
6767     setRowVisibility : function(rowIndex, state)
6768     {
6769         var bt = this.mainBody.dom;
6770         
6771         var rows = this.el.select('tbody > tr', true).elements;
6772         
6773         if(typeof(rows[rowIndex]) == 'undefined'){
6774             return;
6775         }
6776         rows[rowIndex].dom.style.display = state ? '' : 'none';
6777     },
6778     
6779     
6780     getSelectionModel : function(){
6781         if(!this.selModel){
6782             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6783         }
6784         return this.selModel;
6785     },
6786     /*
6787      * Render the Roo.bootstrap object from renderder
6788      */
6789     renderCellObject : function(r)
6790     {
6791         var _this = this;
6792         
6793         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6794         
6795         var t = r.cfg.render(r.container);
6796         
6797         if(r.cfg.cn){
6798             Roo.each(r.cfg.cn, function(c){
6799                 var child = {
6800                     container: t.getChildContainer(),
6801                     cfg: c
6802                 };
6803                 _this.renderCellObject(child);
6804             })
6805         }
6806     },
6807     
6808     getRowIndex : function(row)
6809     {
6810         var rowIndex = -1;
6811         
6812         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6813             if(el != row){
6814                 return;
6815             }
6816             
6817             rowIndex = index;
6818         });
6819         
6820         return rowIndex;
6821     },
6822      /**
6823      * Returns the grid's underlying element = used by panel.Grid
6824      * @return {Element} The element
6825      */
6826     getGridEl : function(){
6827         return this.el;
6828     },
6829      /**
6830      * Forces a resize - used by panel.Grid
6831      * @return {Element} The element
6832      */
6833     autoSize : function()
6834     {
6835         //var ctr = Roo.get(this.container.dom.parentElement);
6836         var ctr = Roo.get(this.el.dom);
6837         
6838         var thd = this.getGridEl().select('thead',true).first();
6839         var tbd = this.getGridEl().select('tbody', true).first();
6840         var tfd = this.getGridEl().select('tfoot', true).first();
6841         
6842         var cw = ctr.getWidth();
6843         
6844         if (tbd) {
6845             
6846             tbd.setSize(ctr.getWidth(),
6847                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6848             );
6849             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6850             cw -= barsize;
6851         }
6852         cw = Math.max(cw, this.totalWidth);
6853         this.getGridEl().select('tr',true).setWidth(cw);
6854         // resize 'expandable coloumn?
6855         
6856         return; // we doe not have a view in this design..
6857         
6858     },
6859     onBodyScroll: function()
6860     {
6861         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6862         this.mainHead.setStyle({
6863             'position' : 'relative',
6864             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6865         });
6866         
6867         if(this.lazyLoad){
6868             
6869             var scrollHeight = this.mainBody.dom.scrollHeight;
6870             
6871             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6872             
6873             var height = this.mainBody.getHeight();
6874             
6875             if(scrollHeight - height == scrollTop) {
6876                 
6877                 var total = this.ds.getTotalCount();
6878                 
6879                 if(this.footer.cursor + this.footer.pageSize < total){
6880                     
6881                     this.footer.ds.load({
6882                         params : {
6883                             start : this.footer.cursor + this.footer.pageSize,
6884                             limit : this.footer.pageSize
6885                         },
6886                         add : true
6887                     });
6888                 }
6889             }
6890             
6891         }
6892     }
6893 });
6894
6895  
6896
6897  /*
6898  * - LGPL
6899  *
6900  * table cell
6901  * 
6902  */
6903
6904 /**
6905  * @class Roo.bootstrap.TableCell
6906  * @extends Roo.bootstrap.Component
6907  * Bootstrap TableCell class
6908  * @cfg {String} html cell contain text
6909  * @cfg {String} cls cell class
6910  * @cfg {String} tag cell tag (td|th) default td
6911  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6912  * @cfg {String} align Aligns the content in a cell
6913  * @cfg {String} axis Categorizes cells
6914  * @cfg {String} bgcolor Specifies the background color of a cell
6915  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6916  * @cfg {Number} colspan Specifies the number of columns a cell should span
6917  * @cfg {String} headers Specifies one or more header cells a cell is related to
6918  * @cfg {Number} height Sets the height of a cell
6919  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6920  * @cfg {Number} rowspan Sets the number of rows a cell should span
6921  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6922  * @cfg {String} valign Vertical aligns the content in a cell
6923  * @cfg {Number} width Specifies the width of a cell
6924  * 
6925  * @constructor
6926  * Create a new TableCell
6927  * @param {Object} config The config object
6928  */
6929
6930 Roo.bootstrap.TableCell = function(config){
6931     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6932 };
6933
6934 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6935     
6936     html: false,
6937     cls: false,
6938     tag: false,
6939     abbr: false,
6940     align: false,
6941     axis: false,
6942     bgcolor: false,
6943     charoff: false,
6944     colspan: false,
6945     headers: false,
6946     height: false,
6947     nowrap: false,
6948     rowspan: false,
6949     scope: false,
6950     valign: false,
6951     width: false,
6952     
6953     
6954     getAutoCreate : function(){
6955         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6956         
6957         cfg = {
6958             tag: 'td'
6959         };
6960         
6961         if(this.tag){
6962             cfg.tag = this.tag;
6963         }
6964         
6965         if (this.html) {
6966             cfg.html=this.html
6967         }
6968         if (this.cls) {
6969             cfg.cls=this.cls
6970         }
6971         if (this.abbr) {
6972             cfg.abbr=this.abbr
6973         }
6974         if (this.align) {
6975             cfg.align=this.align
6976         }
6977         if (this.axis) {
6978             cfg.axis=this.axis
6979         }
6980         if (this.bgcolor) {
6981             cfg.bgcolor=this.bgcolor
6982         }
6983         if (this.charoff) {
6984             cfg.charoff=this.charoff
6985         }
6986         if (this.colspan) {
6987             cfg.colspan=this.colspan
6988         }
6989         if (this.headers) {
6990             cfg.headers=this.headers
6991         }
6992         if (this.height) {
6993             cfg.height=this.height
6994         }
6995         if (this.nowrap) {
6996             cfg.nowrap=this.nowrap
6997         }
6998         if (this.rowspan) {
6999             cfg.rowspan=this.rowspan
7000         }
7001         if (this.scope) {
7002             cfg.scope=this.scope
7003         }
7004         if (this.valign) {
7005             cfg.valign=this.valign
7006         }
7007         if (this.width) {
7008             cfg.width=this.width
7009         }
7010         
7011         
7012         return cfg;
7013     }
7014    
7015 });
7016
7017  
7018
7019  /*
7020  * - LGPL
7021  *
7022  * table row
7023  * 
7024  */
7025
7026 /**
7027  * @class Roo.bootstrap.TableRow
7028  * @extends Roo.bootstrap.Component
7029  * Bootstrap TableRow class
7030  * @cfg {String} cls row class
7031  * @cfg {String} align Aligns the content in a table row
7032  * @cfg {String} bgcolor Specifies a background color for a table row
7033  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7034  * @cfg {String} valign Vertical aligns the content in a table row
7035  * 
7036  * @constructor
7037  * Create a new TableRow
7038  * @param {Object} config The config object
7039  */
7040
7041 Roo.bootstrap.TableRow = function(config){
7042     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7043 };
7044
7045 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7046     
7047     cls: false,
7048     align: false,
7049     bgcolor: false,
7050     charoff: false,
7051     valign: false,
7052     
7053     getAutoCreate : function(){
7054         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7055         
7056         cfg = {
7057             tag: 'tr'
7058         };
7059             
7060         if(this.cls){
7061             cfg.cls = this.cls;
7062         }
7063         if(this.align){
7064             cfg.align = this.align;
7065         }
7066         if(this.bgcolor){
7067             cfg.bgcolor = this.bgcolor;
7068         }
7069         if(this.charoff){
7070             cfg.charoff = this.charoff;
7071         }
7072         if(this.valign){
7073             cfg.valign = this.valign;
7074         }
7075         
7076         return cfg;
7077     }
7078    
7079 });
7080
7081  
7082
7083  /*
7084  * - LGPL
7085  *
7086  * table body
7087  * 
7088  */
7089
7090 /**
7091  * @class Roo.bootstrap.TableBody
7092  * @extends Roo.bootstrap.Component
7093  * Bootstrap TableBody class
7094  * @cfg {String} cls element class
7095  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7096  * @cfg {String} align Aligns the content inside the element
7097  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7098  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7099  * 
7100  * @constructor
7101  * Create a new TableBody
7102  * @param {Object} config The config object
7103  */
7104
7105 Roo.bootstrap.TableBody = function(config){
7106     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7107 };
7108
7109 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7110     
7111     cls: false,
7112     tag: false,
7113     align: false,
7114     charoff: false,
7115     valign: false,
7116     
7117     getAutoCreate : function(){
7118         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7119         
7120         cfg = {
7121             tag: 'tbody'
7122         };
7123             
7124         if (this.cls) {
7125             cfg.cls=this.cls
7126         }
7127         if(this.tag){
7128             cfg.tag = this.tag;
7129         }
7130         
7131         if(this.align){
7132             cfg.align = this.align;
7133         }
7134         if(this.charoff){
7135             cfg.charoff = this.charoff;
7136         }
7137         if(this.valign){
7138             cfg.valign = this.valign;
7139         }
7140         
7141         return cfg;
7142     }
7143     
7144     
7145 //    initEvents : function()
7146 //    {
7147 //        
7148 //        if(!this.store){
7149 //            return;
7150 //        }
7151 //        
7152 //        this.store = Roo.factory(this.store, Roo.data);
7153 //        this.store.on('load', this.onLoad, this);
7154 //        
7155 //        this.store.load();
7156 //        
7157 //    },
7158 //    
7159 //    onLoad: function () 
7160 //    {   
7161 //        this.fireEvent('load', this);
7162 //    }
7163 //    
7164 //   
7165 });
7166
7167  
7168
7169  /*
7170  * Based on:
7171  * Ext JS Library 1.1.1
7172  * Copyright(c) 2006-2007, Ext JS, LLC.
7173  *
7174  * Originally Released Under LGPL - original licence link has changed is not relivant.
7175  *
7176  * Fork - LGPL
7177  * <script type="text/javascript">
7178  */
7179
7180 // as we use this in bootstrap.
7181 Roo.namespace('Roo.form');
7182  /**
7183  * @class Roo.form.Action
7184  * Internal Class used to handle form actions
7185  * @constructor
7186  * @param {Roo.form.BasicForm} el The form element or its id
7187  * @param {Object} config Configuration options
7188  */
7189
7190  
7191  
7192 // define the action interface
7193 Roo.form.Action = function(form, options){
7194     this.form = form;
7195     this.options = options || {};
7196 };
7197 /**
7198  * Client Validation Failed
7199  * @const 
7200  */
7201 Roo.form.Action.CLIENT_INVALID = 'client';
7202 /**
7203  * Server Validation Failed
7204  * @const 
7205  */
7206 Roo.form.Action.SERVER_INVALID = 'server';
7207  /**
7208  * Connect to Server Failed
7209  * @const 
7210  */
7211 Roo.form.Action.CONNECT_FAILURE = 'connect';
7212 /**
7213  * Reading Data from Server Failed
7214  * @const 
7215  */
7216 Roo.form.Action.LOAD_FAILURE = 'load';
7217
7218 Roo.form.Action.prototype = {
7219     type : 'default',
7220     failureType : undefined,
7221     response : undefined,
7222     result : undefined,
7223
7224     // interface method
7225     run : function(options){
7226
7227     },
7228
7229     // interface method
7230     success : function(response){
7231
7232     },
7233
7234     // interface method
7235     handleResponse : function(response){
7236
7237     },
7238
7239     // default connection failure
7240     failure : function(response){
7241         
7242         this.response = response;
7243         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7244         this.form.afterAction(this, false);
7245     },
7246
7247     processResponse : function(response){
7248         this.response = response;
7249         if(!response.responseText){
7250             return true;
7251         }
7252         this.result = this.handleResponse(response);
7253         return this.result;
7254     },
7255
7256     // utility functions used internally
7257     getUrl : function(appendParams){
7258         var url = this.options.url || this.form.url || this.form.el.dom.action;
7259         if(appendParams){
7260             var p = this.getParams();
7261             if(p){
7262                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7263             }
7264         }
7265         return url;
7266     },
7267
7268     getMethod : function(){
7269         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7270     },
7271
7272     getParams : function(){
7273         var bp = this.form.baseParams;
7274         var p = this.options.params;
7275         if(p){
7276             if(typeof p == "object"){
7277                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7278             }else if(typeof p == 'string' && bp){
7279                 p += '&' + Roo.urlEncode(bp);
7280             }
7281         }else if(bp){
7282             p = Roo.urlEncode(bp);
7283         }
7284         return p;
7285     },
7286
7287     createCallback : function(){
7288         return {
7289             success: this.success,
7290             failure: this.failure,
7291             scope: this,
7292             timeout: (this.form.timeout*1000),
7293             upload: this.form.fileUpload ? this.success : undefined
7294         };
7295     }
7296 };
7297
7298 Roo.form.Action.Submit = function(form, options){
7299     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7300 };
7301
7302 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7303     type : 'submit',
7304
7305     haveProgress : false,
7306     uploadComplete : false,
7307     
7308     // uploadProgress indicator.
7309     uploadProgress : function()
7310     {
7311         if (!this.form.progressUrl) {
7312             return;
7313         }
7314         
7315         if (!this.haveProgress) {
7316             Roo.MessageBox.progress("Uploading", "Uploading");
7317         }
7318         if (this.uploadComplete) {
7319            Roo.MessageBox.hide();
7320            return;
7321         }
7322         
7323         this.haveProgress = true;
7324    
7325         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7326         
7327         var c = new Roo.data.Connection();
7328         c.request({
7329             url : this.form.progressUrl,
7330             params: {
7331                 id : uid
7332             },
7333             method: 'GET',
7334             success : function(req){
7335                //console.log(data);
7336                 var rdata = false;
7337                 var edata;
7338                 try  {
7339                    rdata = Roo.decode(req.responseText)
7340                 } catch (e) {
7341                     Roo.log("Invalid data from server..");
7342                     Roo.log(edata);
7343                     return;
7344                 }
7345                 if (!rdata || !rdata.success) {
7346                     Roo.log(rdata);
7347                     Roo.MessageBox.alert(Roo.encode(rdata));
7348                     return;
7349                 }
7350                 var data = rdata.data;
7351                 
7352                 if (this.uploadComplete) {
7353                    Roo.MessageBox.hide();
7354                    return;
7355                 }
7356                    
7357                 if (data){
7358                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7359                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7360                     );
7361                 }
7362                 this.uploadProgress.defer(2000,this);
7363             },
7364        
7365             failure: function(data) {
7366                 Roo.log('progress url failed ');
7367                 Roo.log(data);
7368             },
7369             scope : this
7370         });
7371            
7372     },
7373     
7374     
7375     run : function()
7376     {
7377         // run get Values on the form, so it syncs any secondary forms.
7378         this.form.getValues();
7379         
7380         var o = this.options;
7381         var method = this.getMethod();
7382         var isPost = method == 'POST';
7383         if(o.clientValidation === false || this.form.isValid()){
7384             
7385             if (this.form.progressUrl) {
7386                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7387                     (new Date() * 1) + '' + Math.random());
7388                     
7389             } 
7390             
7391             
7392             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7393                 form:this.form.el.dom,
7394                 url:this.getUrl(!isPost),
7395                 method: method,
7396                 params:isPost ? this.getParams() : null,
7397                 isUpload: this.form.fileUpload
7398             }));
7399             
7400             this.uploadProgress();
7401
7402         }else if (o.clientValidation !== false){ // client validation failed
7403             this.failureType = Roo.form.Action.CLIENT_INVALID;
7404             this.form.afterAction(this, false);
7405         }
7406     },
7407
7408     success : function(response)
7409     {
7410         this.uploadComplete= true;
7411         if (this.haveProgress) {
7412             Roo.MessageBox.hide();
7413         }
7414         
7415         
7416         var result = this.processResponse(response);
7417         if(result === true || result.success){
7418             this.form.afterAction(this, true);
7419             return;
7420         }
7421         if(result.errors){
7422             this.form.markInvalid(result.errors);
7423             this.failureType = Roo.form.Action.SERVER_INVALID;
7424         }
7425         this.form.afterAction(this, false);
7426     },
7427     failure : function(response)
7428     {
7429         this.uploadComplete= true;
7430         if (this.haveProgress) {
7431             Roo.MessageBox.hide();
7432         }
7433         
7434         this.response = response;
7435         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7436         this.form.afterAction(this, false);
7437     },
7438     
7439     handleResponse : function(response){
7440         if(this.form.errorReader){
7441             var rs = this.form.errorReader.read(response);
7442             var errors = [];
7443             if(rs.records){
7444                 for(var i = 0, len = rs.records.length; i < len; i++) {
7445                     var r = rs.records[i];
7446                     errors[i] = r.data;
7447                 }
7448             }
7449             if(errors.length < 1){
7450                 errors = null;
7451             }
7452             return {
7453                 success : rs.success,
7454                 errors : errors
7455             };
7456         }
7457         var ret = false;
7458         try {
7459             ret = Roo.decode(response.responseText);
7460         } catch (e) {
7461             ret = {
7462                 success: false,
7463                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7464                 errors : []
7465             };
7466         }
7467         return ret;
7468         
7469     }
7470 });
7471
7472
7473 Roo.form.Action.Load = function(form, options){
7474     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7475     this.reader = this.form.reader;
7476 };
7477
7478 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7479     type : 'load',
7480
7481     run : function(){
7482         
7483         Roo.Ajax.request(Roo.apply(
7484                 this.createCallback(), {
7485                     method:this.getMethod(),
7486                     url:this.getUrl(false),
7487                     params:this.getParams()
7488         }));
7489     },
7490
7491     success : function(response){
7492         
7493         var result = this.processResponse(response);
7494         if(result === true || !result.success || !result.data){
7495             this.failureType = Roo.form.Action.LOAD_FAILURE;
7496             this.form.afterAction(this, false);
7497             return;
7498         }
7499         this.form.clearInvalid();
7500         this.form.setValues(result.data);
7501         this.form.afterAction(this, true);
7502     },
7503
7504     handleResponse : function(response){
7505         if(this.form.reader){
7506             var rs = this.form.reader.read(response);
7507             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7508             return {
7509                 success : rs.success,
7510                 data : data
7511             };
7512         }
7513         return Roo.decode(response.responseText);
7514     }
7515 });
7516
7517 Roo.form.Action.ACTION_TYPES = {
7518     'load' : Roo.form.Action.Load,
7519     'submit' : Roo.form.Action.Submit
7520 };/*
7521  * - LGPL
7522  *
7523  * form
7524  *
7525  */
7526
7527 /**
7528  * @class Roo.bootstrap.Form
7529  * @extends Roo.bootstrap.Component
7530  * Bootstrap Form class
7531  * @cfg {String} method  GET | POST (default POST)
7532  * @cfg {String} labelAlign top | left (default top)
7533  * @cfg {String} align left  | right - for navbars
7534  * @cfg {Boolean} loadMask load mask when submit (default true)
7535
7536  *
7537  * @constructor
7538  * Create a new Form
7539  * @param {Object} config The config object
7540  */
7541
7542
7543 Roo.bootstrap.Form = function(config){
7544     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7545     
7546     Roo.bootstrap.Form.popover.apply();
7547     
7548     this.addEvents({
7549         /**
7550          * @event clientvalidation
7551          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7552          * @param {Form} this
7553          * @param {Boolean} valid true if the form has passed client-side validation
7554          */
7555         clientvalidation: true,
7556         /**
7557          * @event beforeaction
7558          * Fires before any action is performed. Return false to cancel the action.
7559          * @param {Form} this
7560          * @param {Action} action The action to be performed
7561          */
7562         beforeaction: true,
7563         /**
7564          * @event actionfailed
7565          * Fires when an action fails.
7566          * @param {Form} this
7567          * @param {Action} action The action that failed
7568          */
7569         actionfailed : true,
7570         /**
7571          * @event actioncomplete
7572          * Fires when an action is completed.
7573          * @param {Form} this
7574          * @param {Action} action The action that completed
7575          */
7576         actioncomplete : true
7577     });
7578
7579 };
7580
7581 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7582
7583      /**
7584      * @cfg {String} method
7585      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7586      */
7587     method : 'POST',
7588     /**
7589      * @cfg {String} url
7590      * The URL to use for form actions if one isn't supplied in the action options.
7591      */
7592     /**
7593      * @cfg {Boolean} fileUpload
7594      * Set to true if this form is a file upload.
7595      */
7596
7597     /**
7598      * @cfg {Object} baseParams
7599      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7600      */
7601
7602     /**
7603      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7604      */
7605     timeout: 30,
7606     /**
7607      * @cfg {Sting} align (left|right) for navbar forms
7608      */
7609     align : 'left',
7610
7611     // private
7612     activeAction : null,
7613
7614     /**
7615      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7616      * element by passing it or its id or mask the form itself by passing in true.
7617      * @type Mixed
7618      */
7619     waitMsgTarget : false,
7620
7621     loadMask : true,
7622     
7623     /**
7624      * @cfg {Boolean} errorMask (true|false) default false
7625      */
7626     errorMask : false,
7627     
7628     /**
7629      * @cfg {Number} maskOffset Default 100
7630      */
7631     maskOffset : 100,
7632     
7633     /**
7634      * @cfg {Boolean} maskBody
7635      */
7636     maskBody : false,
7637
7638     getAutoCreate : function(){
7639
7640         var cfg = {
7641             tag: 'form',
7642             method : this.method || 'POST',
7643             id : this.id || Roo.id(),
7644             cls : ''
7645         };
7646         if (this.parent().xtype.match(/^Nav/)) {
7647             cfg.cls = 'navbar-form navbar-' + this.align;
7648
7649         }
7650
7651         if (this.labelAlign == 'left' ) {
7652             cfg.cls += ' form-horizontal';
7653         }
7654
7655
7656         return cfg;
7657     },
7658     initEvents : function()
7659     {
7660         this.el.on('submit', this.onSubmit, this);
7661         // this was added as random key presses on the form where triggering form submit.
7662         this.el.on('keypress', function(e) {
7663             if (e.getCharCode() != 13) {
7664                 return true;
7665             }
7666             // we might need to allow it for textareas.. and some other items.
7667             // check e.getTarget().
7668
7669             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7670                 return true;
7671             }
7672
7673             Roo.log("keypress blocked");
7674
7675             e.preventDefault();
7676             return false;
7677         });
7678         
7679     },
7680     // private
7681     onSubmit : function(e){
7682         e.stopEvent();
7683     },
7684
7685      /**
7686      * Returns true if client-side validation on the form is successful.
7687      * @return Boolean
7688      */
7689     isValid : function(){
7690         var items = this.getItems();
7691         var valid = true;
7692         var target = false;
7693         
7694         items.each(function(f){
7695             if(f.validate()){
7696                 return;
7697             }
7698             valid = false;
7699
7700             if(!target && f.el.isVisible(true)){
7701                 target = f;
7702             }
7703            
7704         });
7705         
7706         if(this.errorMask && !valid){
7707             Roo.bootstrap.Form.popover.mask(this, target);
7708         }
7709         
7710         return valid;
7711     },
7712     
7713     /**
7714      * Returns true if any fields in this form have changed since their original load.
7715      * @return Boolean
7716      */
7717     isDirty : function(){
7718         var dirty = false;
7719         var items = this.getItems();
7720         items.each(function(f){
7721            if(f.isDirty()){
7722                dirty = true;
7723                return false;
7724            }
7725            return true;
7726         });
7727         return dirty;
7728     },
7729      /**
7730      * Performs a predefined action (submit or load) or custom actions you define on this form.
7731      * @param {String} actionName The name of the action type
7732      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7733      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7734      * accept other config options):
7735      * <pre>
7736 Property          Type             Description
7737 ----------------  ---------------  ----------------------------------------------------------------------------------
7738 url               String           The url for the action (defaults to the form's url)
7739 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7740 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7741 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7742                                    validate the form on the client (defaults to false)
7743      * </pre>
7744      * @return {BasicForm} this
7745      */
7746     doAction : function(action, options){
7747         if(typeof action == 'string'){
7748             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7749         }
7750         if(this.fireEvent('beforeaction', this, action) !== false){
7751             this.beforeAction(action);
7752             action.run.defer(100, action);
7753         }
7754         return this;
7755     },
7756
7757     // private
7758     beforeAction : function(action){
7759         var o = action.options;
7760         
7761         if(this.loadMask){
7762             
7763             if(this.maskBody){
7764                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7765             } else {
7766                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7767             }
7768         }
7769         // not really supported yet.. ??
7770
7771         //if(this.waitMsgTarget === true){
7772         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7773         //}else if(this.waitMsgTarget){
7774         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7775         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7776         //}else {
7777         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7778        // }
7779
7780     },
7781
7782     // private
7783     afterAction : function(action, success){
7784         this.activeAction = null;
7785         var o = action.options;
7786
7787         if(this.loadMask){
7788             
7789             if(this.maskBody){
7790                 Roo.get(document.body).unmask();
7791             } else {
7792                 this.el.unmask();
7793             }
7794         }
7795         
7796         //if(this.waitMsgTarget === true){
7797 //            this.el.unmask();
7798         //}else if(this.waitMsgTarget){
7799         //    this.waitMsgTarget.unmask();
7800         //}else{
7801         //    Roo.MessageBox.updateProgress(1);
7802         //    Roo.MessageBox.hide();
7803        // }
7804         //
7805         if(success){
7806             if(o.reset){
7807                 this.reset();
7808             }
7809             Roo.callback(o.success, o.scope, [this, action]);
7810             this.fireEvent('actioncomplete', this, action);
7811
7812         }else{
7813
7814             // failure condition..
7815             // we have a scenario where updates need confirming.
7816             // eg. if a locking scenario exists..
7817             // we look for { errors : { needs_confirm : true }} in the response.
7818             if (
7819                 (typeof(action.result) != 'undefined')  &&
7820                 (typeof(action.result.errors) != 'undefined')  &&
7821                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7822            ){
7823                 var _t = this;
7824                 Roo.log("not supported yet");
7825                  /*
7826
7827                 Roo.MessageBox.confirm(
7828                     "Change requires confirmation",
7829                     action.result.errorMsg,
7830                     function(r) {
7831                         if (r != 'yes') {
7832                             return;
7833                         }
7834                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7835                     }
7836
7837                 );
7838                 */
7839
7840
7841                 return;
7842             }
7843
7844             Roo.callback(o.failure, o.scope, [this, action]);
7845             // show an error message if no failed handler is set..
7846             if (!this.hasListener('actionfailed')) {
7847                 Roo.log("need to add dialog support");
7848                 /*
7849                 Roo.MessageBox.alert("Error",
7850                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7851                         action.result.errorMsg :
7852                         "Saving Failed, please check your entries or try again"
7853                 );
7854                 */
7855             }
7856
7857             this.fireEvent('actionfailed', this, action);
7858         }
7859
7860     },
7861     /**
7862      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7863      * @param {String} id The value to search for
7864      * @return Field
7865      */
7866     findField : function(id){
7867         var items = this.getItems();
7868         var field = items.get(id);
7869         if(!field){
7870              items.each(function(f){
7871                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7872                     field = f;
7873                     return false;
7874                 }
7875                 return true;
7876             });
7877         }
7878         return field || null;
7879     },
7880      /**
7881      * Mark fields in this form invalid in bulk.
7882      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7883      * @return {BasicForm} this
7884      */
7885     markInvalid : function(errors){
7886         if(errors instanceof Array){
7887             for(var i = 0, len = errors.length; i < len; i++){
7888                 var fieldError = errors[i];
7889                 var f = this.findField(fieldError.id);
7890                 if(f){
7891                     f.markInvalid(fieldError.msg);
7892                 }
7893             }
7894         }else{
7895             var field, id;
7896             for(id in errors){
7897                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7898                     field.markInvalid(errors[id]);
7899                 }
7900             }
7901         }
7902         //Roo.each(this.childForms || [], function (f) {
7903         //    f.markInvalid(errors);
7904         //});
7905
7906         return this;
7907     },
7908
7909     /**
7910      * Set values for fields in this form in bulk.
7911      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7912      * @return {BasicForm} this
7913      */
7914     setValues : function(values){
7915         if(values instanceof Array){ // array of objects
7916             for(var i = 0, len = values.length; i < len; i++){
7917                 var v = values[i];
7918                 var f = this.findField(v.id);
7919                 if(f){
7920                     f.setValue(v.value);
7921                     if(this.trackResetOnLoad){
7922                         f.originalValue = f.getValue();
7923                     }
7924                 }
7925             }
7926         }else{ // object hash
7927             var field, id;
7928             for(id in values){
7929                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7930
7931                     if (field.setFromData &&
7932                         field.valueField &&
7933                         field.displayField &&
7934                         // combos' with local stores can
7935                         // be queried via setValue()
7936                         // to set their value..
7937                         (field.store && !field.store.isLocal)
7938                         ) {
7939                         // it's a combo
7940                         var sd = { };
7941                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7942                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7943                         field.setFromData(sd);
7944
7945                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7946                         
7947                         field.setFromData(values);
7948                         
7949                     } else {
7950                         field.setValue(values[id]);
7951                     }
7952
7953
7954                     if(this.trackResetOnLoad){
7955                         field.originalValue = field.getValue();
7956                     }
7957                 }
7958             }
7959         }
7960
7961         //Roo.each(this.childForms || [], function (f) {
7962         //    f.setValues(values);
7963         //});
7964
7965         return this;
7966     },
7967
7968     /**
7969      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7970      * they are returned as an array.
7971      * @param {Boolean} asString
7972      * @return {Object}
7973      */
7974     getValues : function(asString){
7975         //if (this.childForms) {
7976             // copy values from the child forms
7977         //    Roo.each(this.childForms, function (f) {
7978         //        this.setValues(f.getValues());
7979         //    }, this);
7980         //}
7981
7982
7983
7984         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7985         if(asString === true){
7986             return fs;
7987         }
7988         return Roo.urlDecode(fs);
7989     },
7990
7991     /**
7992      * Returns the fields in this form as an object with key/value pairs.
7993      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7994      * @return {Object}
7995      */
7996     getFieldValues : function(with_hidden)
7997     {
7998         var items = this.getItems();
7999         var ret = {};
8000         items.each(function(f){
8001             
8002             if (!f.getName()) {
8003                 return;
8004             }
8005             
8006             var v = f.getValue();
8007             
8008             if (f.inputType =='radio') {
8009                 if (typeof(ret[f.getName()]) == 'undefined') {
8010                     ret[f.getName()] = ''; // empty..
8011                 }
8012
8013                 if (!f.el.dom.checked) {
8014                     return;
8015
8016                 }
8017                 v = f.el.dom.value;
8018
8019             }
8020             
8021             if(f.xtype == 'MoneyField'){
8022                 ret[f.currencyName] = f.getCurrency();
8023             }
8024
8025             // not sure if this supported any more..
8026             if ((typeof(v) == 'object') && f.getRawValue) {
8027                 v = f.getRawValue() ; // dates..
8028             }
8029             // combo boxes where name != hiddenName...
8030             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8031                 ret[f.name] = f.getRawValue();
8032             }
8033             ret[f.getName()] = v;
8034         });
8035
8036         return ret;
8037     },
8038
8039     /**
8040      * Clears all invalid messages in this form.
8041      * @return {BasicForm} this
8042      */
8043     clearInvalid : function(){
8044         var items = this.getItems();
8045
8046         items.each(function(f){
8047            f.clearInvalid();
8048         });
8049
8050
8051
8052         return this;
8053     },
8054
8055     /**
8056      * Resets this form.
8057      * @return {BasicForm} this
8058      */
8059     reset : function(){
8060         var items = this.getItems();
8061         items.each(function(f){
8062             f.reset();
8063         });
8064
8065         Roo.each(this.childForms || [], function (f) {
8066             f.reset();
8067         });
8068
8069
8070         return this;
8071     },
8072     
8073     getItems : function()
8074     {
8075         var r=new Roo.util.MixedCollection(false, function(o){
8076             return o.id || (o.id = Roo.id());
8077         });
8078         var iter = function(el) {
8079             if (el.inputEl) {
8080                 r.add(el);
8081             }
8082             if (!el.items) {
8083                 return;
8084             }
8085             Roo.each(el.items,function(e) {
8086                 iter(e);
8087             });
8088
8089
8090         };
8091
8092         iter(this);
8093         return r;
8094         
8095     }
8096
8097 });
8098
8099 Roo.apply(Roo.bootstrap.Form, {
8100     
8101     popover : {
8102         
8103         padding : 5,
8104         
8105         isApplied : false,
8106         
8107         isMasked : false,
8108         
8109         form : false,
8110         
8111         target : false,
8112         
8113         toolTip : false,
8114         
8115         intervalID : false,
8116         
8117         maskEl : false,
8118         
8119         apply : function()
8120         {
8121             if(this.isApplied){
8122                 return;
8123             }
8124             
8125             this.maskEl = {
8126                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8127                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8128                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8129                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8130             };
8131             
8132             this.maskEl.top.enableDisplayMode("block");
8133             this.maskEl.left.enableDisplayMode("block");
8134             this.maskEl.bottom.enableDisplayMode("block");
8135             this.maskEl.right.enableDisplayMode("block");
8136             
8137             this.toolTip = new Roo.bootstrap.Tooltip({
8138                 cls : 'roo-form-error-popover',
8139                 alignment : {
8140                     'left' : ['r-l', [-2,0], 'right'],
8141                     'right' : ['l-r', [2,0], 'left'],
8142                     'bottom' : ['tl-bl', [0,2], 'top'],
8143                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8144                 }
8145             });
8146             
8147             this.toolTip.render(Roo.get(document.body));
8148
8149             this.toolTip.el.enableDisplayMode("block");
8150             
8151             Roo.get(document.body).on('click', function(){
8152                 this.unmask();
8153             }, this);
8154             
8155             Roo.get(document.body).on('touchstart', function(){
8156                 this.unmask();
8157             }, this);
8158             
8159             this.isApplied = true
8160         },
8161         
8162         mask : function(form, target)
8163         {
8164             this.form = form;
8165             
8166             this.target = target;
8167             
8168             if(!this.form.errorMask || !target.el){
8169                 return;
8170             }
8171             
8172             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8173             
8174             Roo.log(scrollable);
8175             
8176             var ot = this.target.el.calcOffsetsTo(scrollable);
8177             
8178             var scrollTo = ot[1] - this.form.maskOffset;
8179             
8180             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8181             
8182             scrollable.scrollTo('top', scrollTo);
8183             
8184             var box = this.target.el.getBox();
8185             Roo.log(box);
8186             var zIndex = Roo.bootstrap.Modal.zIndex++;
8187
8188             
8189             this.maskEl.top.setStyle('position', 'absolute');
8190             this.maskEl.top.setStyle('z-index', zIndex);
8191             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8192             this.maskEl.top.setLeft(0);
8193             this.maskEl.top.setTop(0);
8194             this.maskEl.top.show();
8195             
8196             this.maskEl.left.setStyle('position', 'absolute');
8197             this.maskEl.left.setStyle('z-index', zIndex);
8198             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8199             this.maskEl.left.setLeft(0);
8200             this.maskEl.left.setTop(box.y - this.padding);
8201             this.maskEl.left.show();
8202
8203             this.maskEl.bottom.setStyle('position', 'absolute');
8204             this.maskEl.bottom.setStyle('z-index', zIndex);
8205             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8206             this.maskEl.bottom.setLeft(0);
8207             this.maskEl.bottom.setTop(box.bottom + this.padding);
8208             this.maskEl.bottom.show();
8209
8210             this.maskEl.right.setStyle('position', 'absolute');
8211             this.maskEl.right.setStyle('z-index', zIndex);
8212             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8213             this.maskEl.right.setLeft(box.right + this.padding);
8214             this.maskEl.right.setTop(box.y - this.padding);
8215             this.maskEl.right.show();
8216
8217             this.toolTip.bindEl = this.target.el;
8218
8219             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8220
8221             var tip = this.target.blankText;
8222
8223             if(this.target.getValue() !== '' ) {
8224                 
8225                 if (this.target.invalidText.length) {
8226                     tip = this.target.invalidText;
8227                 } else if (this.target.regexText.length){
8228                     tip = this.target.regexText;
8229                 }
8230             }
8231
8232             this.toolTip.show(tip);
8233
8234             this.intervalID = window.setInterval(function() {
8235                 Roo.bootstrap.Form.popover.unmask();
8236             }, 10000);
8237
8238             window.onwheel = function(){ return false;};
8239             
8240             (function(){ this.isMasked = true; }).defer(500, this);
8241             
8242         },
8243         
8244         unmask : function()
8245         {
8246             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8247                 return;
8248             }
8249             
8250             this.maskEl.top.setStyle('position', 'absolute');
8251             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8252             this.maskEl.top.hide();
8253
8254             this.maskEl.left.setStyle('position', 'absolute');
8255             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8256             this.maskEl.left.hide();
8257
8258             this.maskEl.bottom.setStyle('position', 'absolute');
8259             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8260             this.maskEl.bottom.hide();
8261
8262             this.maskEl.right.setStyle('position', 'absolute');
8263             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8264             this.maskEl.right.hide();
8265             
8266             this.toolTip.hide();
8267             
8268             this.toolTip.el.hide();
8269             
8270             window.onwheel = function(){ return true;};
8271             
8272             if(this.intervalID){
8273                 window.clearInterval(this.intervalID);
8274                 this.intervalID = false;
8275             }
8276             
8277             this.isMasked = false;
8278             
8279         }
8280         
8281     }
8282     
8283 });
8284
8285 /*
8286  * Based on:
8287  * Ext JS Library 1.1.1
8288  * Copyright(c) 2006-2007, Ext JS, LLC.
8289  *
8290  * Originally Released Under LGPL - original licence link has changed is not relivant.
8291  *
8292  * Fork - LGPL
8293  * <script type="text/javascript">
8294  */
8295 /**
8296  * @class Roo.form.VTypes
8297  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8298  * @singleton
8299  */
8300 Roo.form.VTypes = function(){
8301     // closure these in so they are only created once.
8302     var alpha = /^[a-zA-Z_]+$/;
8303     var alphanum = /^[a-zA-Z0-9_]+$/;
8304     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8305     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8306
8307     // All these messages and functions are configurable
8308     return {
8309         /**
8310          * The function used to validate email addresses
8311          * @param {String} value The email address
8312          */
8313         'email' : function(v){
8314             return email.test(v);
8315         },
8316         /**
8317          * The error text to display when the email validation function returns false
8318          * @type String
8319          */
8320         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8321         /**
8322          * The keystroke filter mask to be applied on email input
8323          * @type RegExp
8324          */
8325         'emailMask' : /[a-z0-9_\.\-@]/i,
8326
8327         /**
8328          * The function used to validate URLs
8329          * @param {String} value The URL
8330          */
8331         'url' : function(v){
8332             return url.test(v);
8333         },
8334         /**
8335          * The error text to display when the url validation function returns false
8336          * @type String
8337          */
8338         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8339         
8340         /**
8341          * The function used to validate alpha values
8342          * @param {String} value The value
8343          */
8344         'alpha' : function(v){
8345             return alpha.test(v);
8346         },
8347         /**
8348          * The error text to display when the alpha validation function returns false
8349          * @type String
8350          */
8351         'alphaText' : 'This field should only contain letters and _',
8352         /**
8353          * The keystroke filter mask to be applied on alpha input
8354          * @type RegExp
8355          */
8356         'alphaMask' : /[a-z_]/i,
8357
8358         /**
8359          * The function used to validate alphanumeric values
8360          * @param {String} value The value
8361          */
8362         'alphanum' : function(v){
8363             return alphanum.test(v);
8364         },
8365         /**
8366          * The error text to display when the alphanumeric validation function returns false
8367          * @type String
8368          */
8369         'alphanumText' : 'This field should only contain letters, numbers and _',
8370         /**
8371          * The keystroke filter mask to be applied on alphanumeric input
8372          * @type RegExp
8373          */
8374         'alphanumMask' : /[a-z0-9_]/i
8375     };
8376 }();/*
8377  * - LGPL
8378  *
8379  * Input
8380  * 
8381  */
8382
8383 /**
8384  * @class Roo.bootstrap.Input
8385  * @extends Roo.bootstrap.Component
8386  * Bootstrap Input class
8387  * @cfg {Boolean} disabled is it disabled
8388  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8389  * @cfg {String} name name of the input
8390  * @cfg {string} fieldLabel - the label associated
8391  * @cfg {string} placeholder - placeholder to put in text.
8392  * @cfg {string}  before - input group add on before
8393  * @cfg {string} after - input group add on after
8394  * @cfg {string} size - (lg|sm) or leave empty..
8395  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8396  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8397  * @cfg {Number} md colspan out of 12 for computer-sized screens
8398  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8399  * @cfg {string} value default value of the input
8400  * @cfg {Number} labelWidth set the width of label 
8401  * @cfg {Number} labellg set the width of label (1-12)
8402  * @cfg {Number} labelmd set the width of label (1-12)
8403  * @cfg {Number} labelsm set the width of label (1-12)
8404  * @cfg {Number} labelxs set the width of label (1-12)
8405  * @cfg {String} labelAlign (top|left)
8406  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8407  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8408  * @cfg {String} indicatorpos (left|right) default left
8409
8410  * @cfg {String} align (left|center|right) Default left
8411  * @cfg {Boolean} forceFeedback (true|false) Default false
8412  * 
8413  * 
8414  * 
8415  * 
8416  * @constructor
8417  * Create a new Input
8418  * @param {Object} config The config object
8419  */
8420
8421 Roo.bootstrap.Input = function(config){
8422     
8423     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8424     
8425     this.addEvents({
8426         /**
8427          * @event focus
8428          * Fires when this field receives input focus.
8429          * @param {Roo.form.Field} this
8430          */
8431         focus : true,
8432         /**
8433          * @event blur
8434          * Fires when this field loses input focus.
8435          * @param {Roo.form.Field} this
8436          */
8437         blur : true,
8438         /**
8439          * @event specialkey
8440          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8441          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8442          * @param {Roo.form.Field} this
8443          * @param {Roo.EventObject} e The event object
8444          */
8445         specialkey : true,
8446         /**
8447          * @event change
8448          * Fires just before the field blurs if the field value has changed.
8449          * @param {Roo.form.Field} this
8450          * @param {Mixed} newValue The new value
8451          * @param {Mixed} oldValue The original value
8452          */
8453         change : true,
8454         /**
8455          * @event invalid
8456          * Fires after the field has been marked as invalid.
8457          * @param {Roo.form.Field} this
8458          * @param {String} msg The validation message
8459          */
8460         invalid : true,
8461         /**
8462          * @event valid
8463          * Fires after the field has been validated with no errors.
8464          * @param {Roo.form.Field} this
8465          */
8466         valid : true,
8467          /**
8468          * @event keyup
8469          * Fires after the key up
8470          * @param {Roo.form.Field} this
8471          * @param {Roo.EventObject}  e The event Object
8472          */
8473         keyup : true
8474     });
8475 };
8476
8477 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8478      /**
8479      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8480       automatic validation (defaults to "keyup").
8481      */
8482     validationEvent : "keyup",
8483      /**
8484      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8485      */
8486     validateOnBlur : true,
8487     /**
8488      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8489      */
8490     validationDelay : 250,
8491      /**
8492      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8493      */
8494     focusClass : "x-form-focus",  // not needed???
8495     
8496        
8497     /**
8498      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8499      */
8500     invalidClass : "has-warning",
8501     
8502     /**
8503      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8504      */
8505     validClass : "has-success",
8506     
8507     /**
8508      * @cfg {Boolean} hasFeedback (true|false) default true
8509      */
8510     hasFeedback : true,
8511     
8512     /**
8513      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8514      */
8515     invalidFeedbackClass : "glyphicon-warning-sign",
8516     
8517     /**
8518      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8519      */
8520     validFeedbackClass : "glyphicon-ok",
8521     
8522     /**
8523      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8524      */
8525     selectOnFocus : false,
8526     
8527      /**
8528      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8529      */
8530     maskRe : null,
8531        /**
8532      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8533      */
8534     vtype : null,
8535     
8536       /**
8537      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8538      */
8539     disableKeyFilter : false,
8540     
8541        /**
8542      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8543      */
8544     disabled : false,
8545      /**
8546      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8547      */
8548     allowBlank : true,
8549     /**
8550      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8551      */
8552     blankText : "Please complete this mandatory field",
8553     
8554      /**
8555      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8556      */
8557     minLength : 0,
8558     /**
8559      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8560      */
8561     maxLength : Number.MAX_VALUE,
8562     /**
8563      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8564      */
8565     minLengthText : "The minimum length for this field is {0}",
8566     /**
8567      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8568      */
8569     maxLengthText : "The maximum length for this field is {0}",
8570   
8571     
8572     /**
8573      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8574      * If available, this function will be called only after the basic validators all return true, and will be passed the
8575      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8576      */
8577     validator : null,
8578     /**
8579      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8580      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8581      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8582      */
8583     regex : null,
8584     /**
8585      * @cfg {String} regexText -- Depricated - use Invalid Text
8586      */
8587     regexText : "",
8588     
8589     /**
8590      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8591      */
8592     invalidText : "",
8593     
8594     
8595     
8596     autocomplete: false,
8597     
8598     
8599     fieldLabel : '',
8600     inputType : 'text',
8601     
8602     name : false,
8603     placeholder: false,
8604     before : false,
8605     after : false,
8606     size : false,
8607     hasFocus : false,
8608     preventMark: false,
8609     isFormField : true,
8610     value : '',
8611     labelWidth : 2,
8612     labelAlign : false,
8613     readOnly : false,
8614     align : false,
8615     formatedValue : false,
8616     forceFeedback : false,
8617     
8618     indicatorpos : 'left',
8619     
8620     labellg : 0,
8621     labelmd : 0,
8622     labelsm : 0,
8623     labelxs : 0,
8624     
8625     parentLabelAlign : function()
8626     {
8627         var parent = this;
8628         while (parent.parent()) {
8629             parent = parent.parent();
8630             if (typeof(parent.labelAlign) !='undefined') {
8631                 return parent.labelAlign;
8632             }
8633         }
8634         return 'left';
8635         
8636     },
8637     
8638     getAutoCreate : function()
8639     {
8640         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8641         
8642         var id = Roo.id();
8643         
8644         var cfg = {};
8645         
8646         if(this.inputType != 'hidden'){
8647             cfg.cls = 'form-group' //input-group
8648         }
8649         
8650         var input =  {
8651             tag: 'input',
8652             id : id,
8653             type : this.inputType,
8654             value : this.value,
8655             cls : 'form-control',
8656             placeholder : this.placeholder || '',
8657             autocomplete : this.autocomplete || 'new-password'
8658         };
8659         
8660         if(this.align){
8661             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8662         }
8663         
8664         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8665             input.maxLength = this.maxLength;
8666         }
8667         
8668         if (this.disabled) {
8669             input.disabled=true;
8670         }
8671         
8672         if (this.readOnly) {
8673             input.readonly=true;
8674         }
8675         
8676         if (this.name) {
8677             input.name = this.name;
8678         }
8679         
8680         if (this.size) {
8681             input.cls += ' input-' + this.size;
8682         }
8683         
8684         var settings=this;
8685         ['xs','sm','md','lg'].map(function(size){
8686             if (settings[size]) {
8687                 cfg.cls += ' col-' + size + '-' + settings[size];
8688             }
8689         });
8690         
8691         var inputblock = input;
8692         
8693         var feedback = {
8694             tag: 'span',
8695             cls: 'glyphicon form-control-feedback'
8696         };
8697             
8698         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8699             
8700             inputblock = {
8701                 cls : 'has-feedback',
8702                 cn :  [
8703                     input,
8704                     feedback
8705                 ] 
8706             };  
8707         }
8708         
8709         if (this.before || this.after) {
8710             
8711             inputblock = {
8712                 cls : 'input-group',
8713                 cn :  [] 
8714             };
8715             
8716             if (this.before && typeof(this.before) == 'string') {
8717                 
8718                 inputblock.cn.push({
8719                     tag :'span',
8720                     cls : 'roo-input-before input-group-addon',
8721                     html : this.before
8722                 });
8723             }
8724             if (this.before && typeof(this.before) == 'object') {
8725                 this.before = Roo.factory(this.before);
8726                 
8727                 inputblock.cn.push({
8728                     tag :'span',
8729                     cls : 'roo-input-before input-group-' +
8730                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8731                 });
8732             }
8733             
8734             inputblock.cn.push(input);
8735             
8736             if (this.after && typeof(this.after) == 'string') {
8737                 inputblock.cn.push({
8738                     tag :'span',
8739                     cls : 'roo-input-after input-group-addon',
8740                     html : this.after
8741                 });
8742             }
8743             if (this.after && typeof(this.after) == 'object') {
8744                 this.after = Roo.factory(this.after);
8745                 
8746                 inputblock.cn.push({
8747                     tag :'span',
8748                     cls : 'roo-input-after input-group-' +
8749                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8750                 });
8751             }
8752             
8753             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8754                 inputblock.cls += ' has-feedback';
8755                 inputblock.cn.push(feedback);
8756             }
8757         };
8758         
8759         if (align ==='left' && this.fieldLabel.length) {
8760             
8761             cfg.cls += ' roo-form-group-label-left';
8762             
8763             cfg.cn = [
8764                 {
8765                     tag : 'i',
8766                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8767                     tooltip : 'This field is required'
8768                 },
8769                 {
8770                     tag: 'label',
8771                     'for' :  id,
8772                     cls : 'control-label',
8773                     html : this.fieldLabel
8774
8775                 },
8776                 {
8777                     cls : "", 
8778                     cn: [
8779                         inputblock
8780                     ]
8781                 }
8782             ];
8783             
8784             var labelCfg = cfg.cn[1];
8785             var contentCfg = cfg.cn[2];
8786             
8787             if(this.indicatorpos == 'right'){
8788                 cfg.cn = [
8789                     {
8790                         tag: 'label',
8791                         'for' :  id,
8792                         cls : 'control-label',
8793                         cn : [
8794                             {
8795                                 tag : 'span',
8796                                 html : this.fieldLabel
8797                             },
8798                             {
8799                                 tag : 'i',
8800                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8801                                 tooltip : 'This field is required'
8802                             }
8803                         ]
8804                     },
8805                     {
8806                         cls : "",
8807                         cn: [
8808                             inputblock
8809                         ]
8810                     }
8811
8812                 ];
8813                 
8814                 labelCfg = cfg.cn[0];
8815                 contentCfg = cfg.cn[1];
8816             
8817             }
8818             
8819             if(this.labelWidth > 12){
8820                 labelCfg.style = "width: " + this.labelWidth + 'px';
8821             }
8822             
8823             if(this.labelWidth < 13 && this.labelmd == 0){
8824                 this.labelmd = this.labelWidth;
8825             }
8826             
8827             if(this.labellg > 0){
8828                 labelCfg.cls += ' col-lg-' + this.labellg;
8829                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8830             }
8831             
8832             if(this.labelmd > 0){
8833                 labelCfg.cls += ' col-md-' + this.labelmd;
8834                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8835             }
8836             
8837             if(this.labelsm > 0){
8838                 labelCfg.cls += ' col-sm-' + this.labelsm;
8839                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8840             }
8841             
8842             if(this.labelxs > 0){
8843                 labelCfg.cls += ' col-xs-' + this.labelxs;
8844                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8845             }
8846             
8847             
8848         } else if ( this.fieldLabel.length) {
8849                 
8850             cfg.cn = [
8851                 {
8852                     tag : 'i',
8853                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8854                     tooltip : 'This field is required'
8855                 },
8856                 {
8857                     tag: 'label',
8858                    //cls : 'input-group-addon',
8859                     html : this.fieldLabel
8860
8861                 },
8862
8863                inputblock
8864
8865            ];
8866            
8867            if(this.indicatorpos == 'right'){
8868                 
8869                 cfg.cn = [
8870                     {
8871                         tag: 'label',
8872                        //cls : 'input-group-addon',
8873                         html : this.fieldLabel
8874
8875                     },
8876                     {
8877                         tag : 'i',
8878                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8879                         tooltip : 'This field is required'
8880                     },
8881
8882                    inputblock
8883
8884                ];
8885
8886             }
8887
8888         } else {
8889             
8890             cfg.cn = [
8891
8892                     inputblock
8893
8894             ];
8895                 
8896                 
8897         };
8898         
8899         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8900            cfg.cls += ' navbar-form';
8901         }
8902         
8903         if (this.parentType === 'NavGroup') {
8904            cfg.cls += ' navbar-form';
8905            cfg.tag = 'li';
8906         }
8907         
8908         return cfg;
8909         
8910     },
8911     /**
8912      * return the real input element.
8913      */
8914     inputEl: function ()
8915     {
8916         return this.el.select('input.form-control',true).first();
8917     },
8918     
8919     tooltipEl : function()
8920     {
8921         return this.inputEl();
8922     },
8923     
8924     indicatorEl : function()
8925     {
8926         var indicator = this.el.select('i.roo-required-indicator',true).first();
8927         
8928         if(!indicator){
8929             return false;
8930         }
8931         
8932         return indicator;
8933         
8934     },
8935     
8936     setDisabled : function(v)
8937     {
8938         var i  = this.inputEl().dom;
8939         if (!v) {
8940             i.removeAttribute('disabled');
8941             return;
8942             
8943         }
8944         i.setAttribute('disabled','true');
8945     },
8946     initEvents : function()
8947     {
8948           
8949         this.inputEl().on("keydown" , this.fireKey,  this);
8950         this.inputEl().on("focus", this.onFocus,  this);
8951         this.inputEl().on("blur", this.onBlur,  this);
8952         
8953         this.inputEl().relayEvent('keyup', this);
8954         
8955         this.indicator = this.indicatorEl();
8956         
8957         if(this.indicator){
8958             this.indicator.addClass('invisible');
8959             
8960         }
8961  
8962         // reference to original value for reset
8963         this.originalValue = this.getValue();
8964         //Roo.form.TextField.superclass.initEvents.call(this);
8965         if(this.validationEvent == 'keyup'){
8966             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8967             this.inputEl().on('keyup', this.filterValidation, this);
8968         }
8969         else if(this.validationEvent !== false){
8970             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8971         }
8972         
8973         if(this.selectOnFocus){
8974             this.on("focus", this.preFocus, this);
8975             
8976         }
8977         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8978             this.inputEl().on("keypress", this.filterKeys, this);
8979         } else {
8980             this.inputEl().relayEvent('keypress', this);
8981         }
8982        /* if(this.grow){
8983             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8984             this.el.on("click", this.autoSize,  this);
8985         }
8986         */
8987         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8988             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8989         }
8990         
8991         if (typeof(this.before) == 'object') {
8992             this.before.render(this.el.select('.roo-input-before',true).first());
8993         }
8994         if (typeof(this.after) == 'object') {
8995             this.after.render(this.el.select('.roo-input-after',true).first());
8996         }
8997         
8998         
8999     },
9000     filterValidation : function(e){
9001         if(!e.isNavKeyPress()){
9002             this.validationTask.delay(this.validationDelay);
9003         }
9004     },
9005      /**
9006      * Validates the field value
9007      * @return {Boolean} True if the value is valid, else false
9008      */
9009     validate : function(){
9010         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9011         if(this.disabled || this.validateValue(this.getRawValue())){
9012             this.markValid();
9013             return true;
9014         }
9015         
9016         this.markInvalid();
9017         return false;
9018     },
9019     
9020     
9021     /**
9022      * Validates a value according to the field's validation rules and marks the field as invalid
9023      * if the validation fails
9024      * @param {Mixed} value The value to validate
9025      * @return {Boolean} True if the value is valid, else false
9026      */
9027     validateValue : function(value){
9028         if(value.length < 1)  { // if it's blank
9029             if(this.allowBlank){
9030                 return true;
9031             }            
9032             return this.inputEl().hasClass('hide') ? true : false;
9033         }
9034         
9035         if(value.length < this.minLength){
9036             return false;
9037         }
9038         if(value.length > this.maxLength){
9039             return false;
9040         }
9041         if(this.vtype){
9042             var vt = Roo.form.VTypes;
9043             if(!vt[this.vtype](value, this)){
9044                 return false;
9045             }
9046         }
9047         if(typeof this.validator == "function"){
9048             var msg = this.validator(value);
9049             if(msg !== true){
9050                 return false;
9051             }
9052             if (typeof(msg) == 'string') {
9053                 this.invalidText = msg;
9054             }
9055         }
9056         
9057         if(this.regex && !this.regex.test(value)){
9058             return false;
9059         }
9060         
9061         return true;
9062     },
9063
9064     
9065     
9066      // private
9067     fireKey : function(e){
9068         //Roo.log('field ' + e.getKey());
9069         if(e.isNavKeyPress()){
9070             this.fireEvent("specialkey", this, e);
9071         }
9072     },
9073     focus : function (selectText){
9074         if(this.rendered){
9075             this.inputEl().focus();
9076             if(selectText === true){
9077                 this.inputEl().dom.select();
9078             }
9079         }
9080         return this;
9081     } ,
9082     
9083     onFocus : function(){
9084         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9085            // this.el.addClass(this.focusClass);
9086         }
9087         if(!this.hasFocus){
9088             this.hasFocus = true;
9089             this.startValue = this.getValue();
9090             this.fireEvent("focus", this);
9091         }
9092     },
9093     
9094     beforeBlur : Roo.emptyFn,
9095
9096     
9097     // private
9098     onBlur : function(){
9099         this.beforeBlur();
9100         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9101             //this.el.removeClass(this.focusClass);
9102         }
9103         this.hasFocus = false;
9104         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9105             this.validate();
9106         }
9107         var v = this.getValue();
9108         if(String(v) !== String(this.startValue)){
9109             this.fireEvent('change', this, v, this.startValue);
9110         }
9111         this.fireEvent("blur", this);
9112     },
9113     
9114     /**
9115      * Resets the current field value to the originally loaded value and clears any validation messages
9116      */
9117     reset : function(){
9118         this.setValue(this.originalValue);
9119         this.validate();
9120     },
9121      /**
9122      * Returns the name of the field
9123      * @return {Mixed} name The name field
9124      */
9125     getName: function(){
9126         return this.name;
9127     },
9128      /**
9129      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9130      * @return {Mixed} value The field value
9131      */
9132     getValue : function(){
9133         
9134         var v = this.inputEl().getValue();
9135         
9136         return v;
9137     },
9138     /**
9139      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9140      * @return {Mixed} value The field value
9141      */
9142     getRawValue : function(){
9143         var v = this.inputEl().getValue();
9144         
9145         return v;
9146     },
9147     
9148     /**
9149      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9150      * @param {Mixed} value The value to set
9151      */
9152     setRawValue : function(v){
9153         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9154     },
9155     
9156     selectText : function(start, end){
9157         var v = this.getRawValue();
9158         if(v.length > 0){
9159             start = start === undefined ? 0 : start;
9160             end = end === undefined ? v.length : end;
9161             var d = this.inputEl().dom;
9162             if(d.setSelectionRange){
9163                 d.setSelectionRange(start, end);
9164             }else if(d.createTextRange){
9165                 var range = d.createTextRange();
9166                 range.moveStart("character", start);
9167                 range.moveEnd("character", v.length-end);
9168                 range.select();
9169             }
9170         }
9171     },
9172     
9173     /**
9174      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9175      * @param {Mixed} value The value to set
9176      */
9177     setValue : function(v){
9178         this.value = v;
9179         if(this.rendered){
9180             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9181             this.validate();
9182         }
9183     },
9184     
9185     /*
9186     processValue : function(value){
9187         if(this.stripCharsRe){
9188             var newValue = value.replace(this.stripCharsRe, '');
9189             if(newValue !== value){
9190                 this.setRawValue(newValue);
9191                 return newValue;
9192             }
9193         }
9194         return value;
9195     },
9196   */
9197     preFocus : function(){
9198         
9199         if(this.selectOnFocus){
9200             this.inputEl().dom.select();
9201         }
9202     },
9203     filterKeys : function(e){
9204         var k = e.getKey();
9205         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9206             return;
9207         }
9208         var c = e.getCharCode(), cc = String.fromCharCode(c);
9209         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9210             return;
9211         }
9212         if(!this.maskRe.test(cc)){
9213             e.stopEvent();
9214         }
9215     },
9216      /**
9217      * Clear any invalid styles/messages for this field
9218      */
9219     clearInvalid : function(){
9220         
9221         if(!this.el || this.preventMark){ // not rendered
9222             return;
9223         }
9224         
9225      
9226         this.el.removeClass(this.invalidClass);
9227         
9228         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9229             
9230             var feedback = this.el.select('.form-control-feedback', true).first();
9231             
9232             if(feedback){
9233                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9234             }
9235             
9236         }
9237         
9238         this.fireEvent('valid', this);
9239     },
9240     
9241      /**
9242      * Mark this field as valid
9243      */
9244     markValid : function()
9245     {
9246         if(!this.el  || this.preventMark){ // not rendered...
9247             return;
9248         }
9249         
9250         this.el.removeClass([this.invalidClass, this.validClass]);
9251         
9252         var feedback = this.el.select('.form-control-feedback', true).first();
9253             
9254         if(feedback){
9255             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9256         }
9257
9258         if(this.disabled){
9259             return;
9260         }
9261         
9262         if(this.allowBlank && !this.getRawValue().length){
9263             return;
9264         }
9265         
9266         if(this.indicator){
9267             this.indicator.removeClass('visible');
9268             this.indicator.addClass('invisible');
9269         }
9270         
9271         this.el.addClass(this.validClass);
9272         
9273         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9274             
9275             var feedback = this.el.select('.form-control-feedback', true).first();
9276             
9277             if(feedback){
9278                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9279                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9280             }
9281             
9282         }
9283         
9284         this.fireEvent('valid', this);
9285     },
9286     
9287      /**
9288      * Mark this field as invalid
9289      * @param {String} msg The validation message
9290      */
9291     markInvalid : function(msg)
9292     {
9293         if(!this.el  || this.preventMark){ // not rendered
9294             return;
9295         }
9296         
9297         this.el.removeClass([this.invalidClass, this.validClass]);
9298         
9299         var feedback = this.el.select('.form-control-feedback', true).first();
9300             
9301         if(feedback){
9302             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9303         }
9304
9305         if(this.disabled){
9306             return;
9307         }
9308         
9309         if(this.allowBlank && !this.getRawValue().length){
9310             return;
9311         }
9312         
9313         if(this.indicator){
9314             this.indicator.removeClass('invisible');
9315             this.indicator.addClass('visible');
9316         }
9317         
9318         this.el.addClass(this.invalidClass);
9319         
9320         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9321             
9322             var feedback = this.el.select('.form-control-feedback', true).first();
9323             
9324             if(feedback){
9325                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9326                 
9327                 if(this.getValue().length || this.forceFeedback){
9328                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9329                 }
9330                 
9331             }
9332             
9333         }
9334         
9335         this.fireEvent('invalid', this, msg);
9336     },
9337     // private
9338     SafariOnKeyDown : function(event)
9339     {
9340         // this is a workaround for a password hang bug on chrome/ webkit.
9341         if (this.inputEl().dom.type != 'password') {
9342             return;
9343         }
9344         
9345         var isSelectAll = false;
9346         
9347         if(this.inputEl().dom.selectionEnd > 0){
9348             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9349         }
9350         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9351             event.preventDefault();
9352             this.setValue('');
9353             return;
9354         }
9355         
9356         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9357             
9358             event.preventDefault();
9359             // this is very hacky as keydown always get's upper case.
9360             //
9361             var cc = String.fromCharCode(event.getCharCode());
9362             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9363             
9364         }
9365     },
9366     adjustWidth : function(tag, w){
9367         tag = tag.toLowerCase();
9368         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9369             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9370                 if(tag == 'input'){
9371                     return w + 2;
9372                 }
9373                 if(tag == 'textarea'){
9374                     return w-2;
9375                 }
9376             }else if(Roo.isOpera){
9377                 if(tag == 'input'){
9378                     return w + 2;
9379                 }
9380                 if(tag == 'textarea'){
9381                     return w-2;
9382                 }
9383             }
9384         }
9385         return w;
9386     },
9387     
9388     setFieldLabel : function(v)
9389     {
9390         if(!this.rendered){
9391             return;
9392         }
9393         
9394         this.fieldLabel = v;
9395         
9396         if(this.indicator){
9397             var ar = this.el.select('label > span',true);
9398             if (!ar.elements.length) {
9399                 Roo.log("could not find label > span on element");
9400                 Roo.log(this);
9401                 return;
9402             }
9403             this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9404             return;
9405         }
9406         
9407         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9408     }
9409 });
9410
9411  
9412 /*
9413  * - LGPL
9414  *
9415  * Input
9416  * 
9417  */
9418
9419 /**
9420  * @class Roo.bootstrap.TextArea
9421  * @extends Roo.bootstrap.Input
9422  * Bootstrap TextArea class
9423  * @cfg {Number} cols Specifies the visible width of a text area
9424  * @cfg {Number} rows Specifies the visible number of lines in a text area
9425  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9426  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9427  * @cfg {string} html text
9428  * 
9429  * @constructor
9430  * Create a new TextArea
9431  * @param {Object} config The config object
9432  */
9433
9434 Roo.bootstrap.TextArea = function(config){
9435     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9436    
9437 };
9438
9439 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9440      
9441     cols : false,
9442     rows : 5,
9443     readOnly : false,
9444     warp : 'soft',
9445     resize : false,
9446     value: false,
9447     html: false,
9448     
9449     getAutoCreate : function(){
9450         
9451         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9452         
9453         var id = Roo.id();
9454         
9455         var cfg = {};
9456         
9457         if(this.inputType != 'hidden'){
9458             cfg.cls = 'form-group' //input-group
9459         }
9460         
9461         var input =  {
9462             tag: 'textarea',
9463             id : id,
9464             warp : this.warp,
9465             rows : this.rows,
9466             value : this.value || '',
9467             html: this.html || '',
9468             cls : 'form-control',
9469             placeholder : this.placeholder || '' 
9470             
9471         };
9472         
9473         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9474             input.maxLength = this.maxLength;
9475         }
9476         
9477         if(this.resize){
9478             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9479         }
9480         
9481         if(this.cols){
9482             input.cols = this.cols;
9483         }
9484         
9485         if (this.readOnly) {
9486             input.readonly = true;
9487         }
9488         
9489         if (this.name) {
9490             input.name = this.name;
9491         }
9492         
9493         if (this.size) {
9494             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9495         }
9496         
9497         var settings=this;
9498         ['xs','sm','md','lg'].map(function(size){
9499             if (settings[size]) {
9500                 cfg.cls += ' col-' + size + '-' + settings[size];
9501             }
9502         });
9503         
9504         var inputblock = input;
9505         
9506         if(this.hasFeedback && !this.allowBlank){
9507             
9508             var feedback = {
9509                 tag: 'span',
9510                 cls: 'glyphicon form-control-feedback'
9511             };
9512
9513             inputblock = {
9514                 cls : 'has-feedback',
9515                 cn :  [
9516                     input,
9517                     feedback
9518                 ] 
9519             };  
9520         }
9521         
9522         
9523         if (this.before || this.after) {
9524             
9525             inputblock = {
9526                 cls : 'input-group',
9527                 cn :  [] 
9528             };
9529             if (this.before) {
9530                 inputblock.cn.push({
9531                     tag :'span',
9532                     cls : 'input-group-addon',
9533                     html : this.before
9534                 });
9535             }
9536             
9537             inputblock.cn.push(input);
9538             
9539             if(this.hasFeedback && !this.allowBlank){
9540                 inputblock.cls += ' has-feedback';
9541                 inputblock.cn.push(feedback);
9542             }
9543             
9544             if (this.after) {
9545                 inputblock.cn.push({
9546                     tag :'span',
9547                     cls : 'input-group-addon',
9548                     html : this.after
9549                 });
9550             }
9551             
9552         }
9553         
9554         if (align ==='left' && this.fieldLabel.length) {
9555             cfg.cn = [
9556                 {
9557                     tag: 'label',
9558                     'for' :  id,
9559                     cls : 'control-label',
9560                     html : this.fieldLabel
9561                 },
9562                 {
9563                     cls : "",
9564                     cn: [
9565                         inputblock
9566                     ]
9567                 }
9568
9569             ];
9570             
9571             if(this.labelWidth > 12){
9572                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9573             }
9574
9575             if(this.labelWidth < 13 && this.labelmd == 0){
9576                 this.labelmd = this.labelWidth;
9577             }
9578
9579             if(this.labellg > 0){
9580                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9581                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9582             }
9583
9584             if(this.labelmd > 0){
9585                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9586                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9587             }
9588
9589             if(this.labelsm > 0){
9590                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9591                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9592             }
9593
9594             if(this.labelxs > 0){
9595                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9596                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9597             }
9598             
9599         } else if ( this.fieldLabel.length) {
9600             cfg.cn = [
9601
9602                {
9603                    tag: 'label',
9604                    //cls : 'input-group-addon',
9605                    html : this.fieldLabel
9606
9607                },
9608
9609                inputblock
9610
9611            ];
9612
9613         } else {
9614
9615             cfg.cn = [
9616
9617                 inputblock
9618
9619             ];
9620                 
9621         }
9622         
9623         if (this.disabled) {
9624             input.disabled=true;
9625         }
9626         
9627         return cfg;
9628         
9629     },
9630     /**
9631      * return the real textarea element.
9632      */
9633     inputEl: function ()
9634     {
9635         return this.el.select('textarea.form-control',true).first();
9636     },
9637     
9638     /**
9639      * Clear any invalid styles/messages for this field
9640      */
9641     clearInvalid : function()
9642     {
9643         
9644         if(!this.el || this.preventMark){ // not rendered
9645             return;
9646         }
9647         
9648         var label = this.el.select('label', true).first();
9649         var icon = this.el.select('i.fa-star', true).first();
9650         
9651         if(label && icon){
9652             icon.remove();
9653         }
9654         
9655         this.el.removeClass(this.invalidClass);
9656         
9657         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9658             
9659             var feedback = this.el.select('.form-control-feedback', true).first();
9660             
9661             if(feedback){
9662                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9663             }
9664             
9665         }
9666         
9667         this.fireEvent('valid', this);
9668     },
9669     
9670      /**
9671      * Mark this field as valid
9672      */
9673     markValid : function()
9674     {
9675         if(!this.el  || this.preventMark){ // not rendered
9676             return;
9677         }
9678         
9679         this.el.removeClass([this.invalidClass, this.validClass]);
9680         
9681         var feedback = this.el.select('.form-control-feedback', true).first();
9682             
9683         if(feedback){
9684             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9685         }
9686
9687         if(this.disabled || this.allowBlank){
9688             return;
9689         }
9690         
9691         var label = this.el.select('label', true).first();
9692         var icon = this.el.select('i.fa-star', true).first();
9693         
9694         if(label && icon){
9695             icon.remove();
9696         }
9697         
9698         this.el.addClass(this.validClass);
9699         
9700         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9701             
9702             var feedback = this.el.select('.form-control-feedback', true).first();
9703             
9704             if(feedback){
9705                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9706                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9707             }
9708             
9709         }
9710         
9711         this.fireEvent('valid', this);
9712     },
9713     
9714      /**
9715      * Mark this field as invalid
9716      * @param {String} msg The validation message
9717      */
9718     markInvalid : function(msg)
9719     {
9720         if(!this.el  || this.preventMark){ // not rendered
9721             return;
9722         }
9723         
9724         this.el.removeClass([this.invalidClass, this.validClass]);
9725         
9726         var feedback = this.el.select('.form-control-feedback', true).first();
9727             
9728         if(feedback){
9729             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9730         }
9731
9732         if(this.disabled || this.allowBlank){
9733             return;
9734         }
9735         
9736         var label = this.el.select('label', true).first();
9737         var icon = this.el.select('i.fa-star', true).first();
9738         
9739         if(!this.getValue().length && label && !icon){
9740             this.el.createChild({
9741                 tag : 'i',
9742                 cls : 'text-danger fa fa-lg fa-star',
9743                 tooltip : 'This field is required',
9744                 style : 'margin-right:5px;'
9745             }, label, true);
9746         }
9747
9748         this.el.addClass(this.invalidClass);
9749         
9750         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9751             
9752             var feedback = this.el.select('.form-control-feedback', true).first();
9753             
9754             if(feedback){
9755                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9756                 
9757                 if(this.getValue().length || this.forceFeedback){
9758                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9759                 }
9760                 
9761             }
9762             
9763         }
9764         
9765         this.fireEvent('invalid', this, msg);
9766     }
9767 });
9768
9769  
9770 /*
9771  * - LGPL
9772  *
9773  * trigger field - base class for combo..
9774  * 
9775  */
9776  
9777 /**
9778  * @class Roo.bootstrap.TriggerField
9779  * @extends Roo.bootstrap.Input
9780  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9781  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9782  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9783  * for which you can provide a custom implementation.  For example:
9784  * <pre><code>
9785 var trigger = new Roo.bootstrap.TriggerField();
9786 trigger.onTriggerClick = myTriggerFn;
9787 trigger.applyTo('my-field');
9788 </code></pre>
9789  *
9790  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9791  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9792  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9793  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9794  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9795
9796  * @constructor
9797  * Create a new TriggerField.
9798  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9799  * to the base TextField)
9800  */
9801 Roo.bootstrap.TriggerField = function(config){
9802     this.mimicing = false;
9803     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9804 };
9805
9806 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9807     /**
9808      * @cfg {String} triggerClass A CSS class to apply to the trigger
9809      */
9810      /**
9811      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9812      */
9813     hideTrigger:false,
9814
9815     /**
9816      * @cfg {Boolean} removable (true|false) special filter default false
9817      */
9818     removable : false,
9819     
9820     /** @cfg {Boolean} grow @hide */
9821     /** @cfg {Number} growMin @hide */
9822     /** @cfg {Number} growMax @hide */
9823
9824     /**
9825      * @hide 
9826      * @method
9827      */
9828     autoSize: Roo.emptyFn,
9829     // private
9830     monitorTab : true,
9831     // private
9832     deferHeight : true,
9833
9834     
9835     actionMode : 'wrap',
9836     
9837     caret : false,
9838     
9839     
9840     getAutoCreate : function(){
9841        
9842         var align = this.labelAlign || this.parentLabelAlign();
9843         
9844         var id = Roo.id();
9845         
9846         var cfg = {
9847             cls: 'form-group' //input-group
9848         };
9849         
9850         
9851         var input =  {
9852             tag: 'input',
9853             id : id,
9854             type : this.inputType,
9855             cls : 'form-control',
9856             autocomplete: 'new-password',
9857             placeholder : this.placeholder || '' 
9858             
9859         };
9860         if (this.name) {
9861             input.name = this.name;
9862         }
9863         if (this.size) {
9864             input.cls += ' input-' + this.size;
9865         }
9866         
9867         if (this.disabled) {
9868             input.disabled=true;
9869         }
9870         
9871         var inputblock = input;
9872         
9873         if(this.hasFeedback && !this.allowBlank){
9874             
9875             var feedback = {
9876                 tag: 'span',
9877                 cls: 'glyphicon form-control-feedback'
9878             };
9879             
9880             if(this.removable && !this.editable && !this.tickable){
9881                 inputblock = {
9882                     cls : 'has-feedback',
9883                     cn :  [
9884                         inputblock,
9885                         {
9886                             tag: 'button',
9887                             html : 'x',
9888                             cls : 'roo-combo-removable-btn close'
9889                         },
9890                         feedback
9891                     ] 
9892                 };
9893             } else {
9894                 inputblock = {
9895                     cls : 'has-feedback',
9896                     cn :  [
9897                         inputblock,
9898                         feedback
9899                     ] 
9900                 };
9901             }
9902
9903         } else {
9904             if(this.removable && !this.editable && !this.tickable){
9905                 inputblock = {
9906                     cls : 'roo-removable',
9907                     cn :  [
9908                         inputblock,
9909                         {
9910                             tag: 'button',
9911                             html : 'x',
9912                             cls : 'roo-combo-removable-btn close'
9913                         }
9914                     ] 
9915                 };
9916             }
9917         }
9918         
9919         if (this.before || this.after) {
9920             
9921             inputblock = {
9922                 cls : 'input-group',
9923                 cn :  [] 
9924             };
9925             if (this.before) {
9926                 inputblock.cn.push({
9927                     tag :'span',
9928                     cls : 'input-group-addon',
9929                     html : this.before
9930                 });
9931             }
9932             
9933             inputblock.cn.push(input);
9934             
9935             if(this.hasFeedback && !this.allowBlank){
9936                 inputblock.cls += ' has-feedback';
9937                 inputblock.cn.push(feedback);
9938             }
9939             
9940             if (this.after) {
9941                 inputblock.cn.push({
9942                     tag :'span',
9943                     cls : 'input-group-addon',
9944                     html : this.after
9945                 });
9946             }
9947             
9948         };
9949         
9950         var box = {
9951             tag: 'div',
9952             cn: [
9953                 {
9954                     tag: 'input',
9955                     type : 'hidden',
9956                     cls: 'form-hidden-field'
9957                 },
9958                 inputblock
9959             ]
9960             
9961         };
9962         
9963         if(this.multiple){
9964             box = {
9965                 tag: 'div',
9966                 cn: [
9967                     {
9968                         tag: 'input',
9969                         type : 'hidden',
9970                         cls: 'form-hidden-field'
9971                     },
9972                     {
9973                         tag: 'ul',
9974                         cls: 'roo-select2-choices',
9975                         cn:[
9976                             {
9977                                 tag: 'li',
9978                                 cls: 'roo-select2-search-field',
9979                                 cn: [
9980
9981                                     inputblock
9982                                 ]
9983                             }
9984                         ]
9985                     }
9986                 ]
9987             }
9988         };
9989         
9990         var combobox = {
9991             cls: 'roo-select2-container input-group',
9992             cn: [
9993                 box
9994 //                {
9995 //                    tag: 'ul',
9996 //                    cls: 'typeahead typeahead-long dropdown-menu',
9997 //                    style: 'display:none'
9998 //                }
9999             ]
10000         };
10001         
10002         if(!this.multiple && this.showToggleBtn){
10003             
10004             var caret = {
10005                         tag: 'span',
10006                         cls: 'caret'
10007              };
10008             if (this.caret != false) {
10009                 caret = {
10010                      tag: 'i',
10011                      cls: 'fa fa-' + this.caret
10012                 };
10013                 
10014             }
10015             
10016             combobox.cn.push({
10017                 tag :'span',
10018                 cls : 'input-group-addon btn dropdown-toggle',
10019                 cn : [
10020                     caret,
10021                     {
10022                         tag: 'span',
10023                         cls: 'combobox-clear',
10024                         cn  : [
10025                             {
10026                                 tag : 'i',
10027                                 cls: 'icon-remove'
10028                             }
10029                         ]
10030                     }
10031                 ]
10032
10033             })
10034         }
10035         
10036         if(this.multiple){
10037             combobox.cls += ' roo-select2-container-multi';
10038         }
10039         
10040         if (align ==='left' && this.fieldLabel.length) {
10041             
10042             cfg.cls += ' roo-form-group-label-left';
10043
10044             cfg.cn = [
10045                 {
10046                     tag : 'i',
10047                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10048                     tooltip : 'This field is required'
10049                 },
10050                 {
10051                     tag: 'label',
10052                     'for' :  id,
10053                     cls : 'control-label',
10054                     html : this.fieldLabel
10055
10056                 },
10057                 {
10058                     cls : "", 
10059                     cn: [
10060                         combobox
10061                     ]
10062                 }
10063
10064             ];
10065             
10066             var labelCfg = cfg.cn[1];
10067             var contentCfg = cfg.cn[2];
10068             
10069             if(this.indicatorpos == 'right'){
10070                 cfg.cn = [
10071                     {
10072                         tag: 'label',
10073                         'for' :  id,
10074                         cls : 'control-label',
10075                         cn : [
10076                             {
10077                                 tag : 'span',
10078                                 html : this.fieldLabel
10079                             },
10080                             {
10081                                 tag : 'i',
10082                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10083                                 tooltip : 'This field is required'
10084                             }
10085                         ]
10086                     },
10087                     {
10088                         cls : "", 
10089                         cn: [
10090                             combobox
10091                         ]
10092                     }
10093
10094                 ];
10095                 
10096                 labelCfg = cfg.cn[0];
10097                 contentCfg = cfg.cn[1];
10098             }
10099             
10100             if(this.labelWidth > 12){
10101                 labelCfg.style = "width: " + this.labelWidth + 'px';
10102             }
10103             
10104             if(this.labelWidth < 13 && this.labelmd == 0){
10105                 this.labelmd = this.labelWidth;
10106             }
10107             
10108             if(this.labellg > 0){
10109                 labelCfg.cls += ' col-lg-' + this.labellg;
10110                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10111             }
10112             
10113             if(this.labelmd > 0){
10114                 labelCfg.cls += ' col-md-' + this.labelmd;
10115                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10116             }
10117             
10118             if(this.labelsm > 0){
10119                 labelCfg.cls += ' col-sm-' + this.labelsm;
10120                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10121             }
10122             
10123             if(this.labelxs > 0){
10124                 labelCfg.cls += ' col-xs-' + this.labelxs;
10125                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10126             }
10127             
10128         } else if ( this.fieldLabel.length) {
10129 //                Roo.log(" label");
10130             cfg.cn = [
10131                 {
10132                    tag : 'i',
10133                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10134                    tooltip : 'This field is required'
10135                },
10136                {
10137                    tag: 'label',
10138                    //cls : 'input-group-addon',
10139                    html : this.fieldLabel
10140
10141                },
10142
10143                combobox
10144
10145             ];
10146             
10147             if(this.indicatorpos == 'right'){
10148                 
10149                 cfg.cn = [
10150                     {
10151                        tag: 'label',
10152                        cn : [
10153                            {
10154                                tag : 'span',
10155                                html : this.fieldLabel
10156                            },
10157                            {
10158                               tag : 'i',
10159                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10160                               tooltip : 'This field is required'
10161                            }
10162                        ]
10163
10164                     },
10165                     combobox
10166
10167                 ];
10168
10169             }
10170
10171         } else {
10172             
10173 //                Roo.log(" no label && no align");
10174                 cfg = combobox
10175                      
10176                 
10177         }
10178         
10179         var settings=this;
10180         ['xs','sm','md','lg'].map(function(size){
10181             if (settings[size]) {
10182                 cfg.cls += ' col-' + size + '-' + settings[size];
10183             }
10184         });
10185         
10186         return cfg;
10187         
10188     },
10189     
10190     
10191     
10192     // private
10193     onResize : function(w, h){
10194 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10195 //        if(typeof w == 'number'){
10196 //            var x = w - this.trigger.getWidth();
10197 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10198 //            this.trigger.setStyle('left', x+'px');
10199 //        }
10200     },
10201
10202     // private
10203     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10204
10205     // private
10206     getResizeEl : function(){
10207         return this.inputEl();
10208     },
10209
10210     // private
10211     getPositionEl : function(){
10212         return this.inputEl();
10213     },
10214
10215     // private
10216     alignErrorIcon : function(){
10217         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10218     },
10219
10220     // private
10221     initEvents : function(){
10222         
10223         this.createList();
10224         
10225         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10226         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10227         if(!this.multiple && this.showToggleBtn){
10228             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10229             if(this.hideTrigger){
10230                 this.trigger.setDisplayed(false);
10231             }
10232             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10233         }
10234         
10235         if(this.multiple){
10236             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10237         }
10238         
10239         if(this.removable && !this.editable && !this.tickable){
10240             var close = this.closeTriggerEl();
10241             
10242             if(close){
10243                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10244                 close.on('click', this.removeBtnClick, this, close);
10245             }
10246         }
10247         
10248         //this.trigger.addClassOnOver('x-form-trigger-over');
10249         //this.trigger.addClassOnClick('x-form-trigger-click');
10250         
10251         //if(!this.width){
10252         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10253         //}
10254     },
10255     
10256     closeTriggerEl : function()
10257     {
10258         var close = this.el.select('.roo-combo-removable-btn', true).first();
10259         return close ? close : false;
10260     },
10261     
10262     removeBtnClick : function(e, h, el)
10263     {
10264         e.preventDefault();
10265         
10266         if(this.fireEvent("remove", this) !== false){
10267             this.reset();
10268             this.fireEvent("afterremove", this)
10269         }
10270     },
10271     
10272     createList : function()
10273     {
10274         this.list = Roo.get(document.body).createChild({
10275             tag: 'ul',
10276             cls: 'typeahead typeahead-long dropdown-menu',
10277             style: 'display:none'
10278         });
10279         
10280         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10281         
10282     },
10283
10284     // private
10285     initTrigger : function(){
10286        
10287     },
10288
10289     // private
10290     onDestroy : function(){
10291         if(this.trigger){
10292             this.trigger.removeAllListeners();
10293           //  this.trigger.remove();
10294         }
10295         //if(this.wrap){
10296         //    this.wrap.remove();
10297         //}
10298         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10299     },
10300
10301     // private
10302     onFocus : function(){
10303         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10304         /*
10305         if(!this.mimicing){
10306             this.wrap.addClass('x-trigger-wrap-focus');
10307             this.mimicing = true;
10308             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10309             if(this.monitorTab){
10310                 this.el.on("keydown", this.checkTab, this);
10311             }
10312         }
10313         */
10314     },
10315
10316     // private
10317     checkTab : function(e){
10318         if(e.getKey() == e.TAB){
10319             this.triggerBlur();
10320         }
10321     },
10322
10323     // private
10324     onBlur : function(){
10325         // do nothing
10326     },
10327
10328     // private
10329     mimicBlur : function(e, t){
10330         /*
10331         if(!this.wrap.contains(t) && this.validateBlur()){
10332             this.triggerBlur();
10333         }
10334         */
10335     },
10336
10337     // private
10338     triggerBlur : function(){
10339         this.mimicing = false;
10340         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10341         if(this.monitorTab){
10342             this.el.un("keydown", this.checkTab, this);
10343         }
10344         //this.wrap.removeClass('x-trigger-wrap-focus');
10345         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10346     },
10347
10348     // private
10349     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10350     validateBlur : function(e, t){
10351         return true;
10352     },
10353
10354     // private
10355     onDisable : function(){
10356         this.inputEl().dom.disabled = true;
10357         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10358         //if(this.wrap){
10359         //    this.wrap.addClass('x-item-disabled');
10360         //}
10361     },
10362
10363     // private
10364     onEnable : function(){
10365         this.inputEl().dom.disabled = false;
10366         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10367         //if(this.wrap){
10368         //    this.el.removeClass('x-item-disabled');
10369         //}
10370     },
10371
10372     // private
10373     onShow : function(){
10374         var ae = this.getActionEl();
10375         
10376         if(ae){
10377             ae.dom.style.display = '';
10378             ae.dom.style.visibility = 'visible';
10379         }
10380     },
10381
10382     // private
10383     
10384     onHide : function(){
10385         var ae = this.getActionEl();
10386         ae.dom.style.display = 'none';
10387     },
10388
10389     /**
10390      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10391      * by an implementing function.
10392      * @method
10393      * @param {EventObject} e
10394      */
10395     onTriggerClick : Roo.emptyFn
10396 });
10397  /*
10398  * Based on:
10399  * Ext JS Library 1.1.1
10400  * Copyright(c) 2006-2007, Ext JS, LLC.
10401  *
10402  * Originally Released Under LGPL - original licence link has changed is not relivant.
10403  *
10404  * Fork - LGPL
10405  * <script type="text/javascript">
10406  */
10407
10408
10409 /**
10410  * @class Roo.data.SortTypes
10411  * @singleton
10412  * Defines the default sorting (casting?) comparison functions used when sorting data.
10413  */
10414 Roo.data.SortTypes = {
10415     /**
10416      * Default sort that does nothing
10417      * @param {Mixed} s The value being converted
10418      * @return {Mixed} The comparison value
10419      */
10420     none : function(s){
10421         return s;
10422     },
10423     
10424     /**
10425      * The regular expression used to strip tags
10426      * @type {RegExp}
10427      * @property
10428      */
10429     stripTagsRE : /<\/?[^>]+>/gi,
10430     
10431     /**
10432      * Strips all HTML tags to sort on text only
10433      * @param {Mixed} s The value being converted
10434      * @return {String} The comparison value
10435      */
10436     asText : function(s){
10437         return String(s).replace(this.stripTagsRE, "");
10438     },
10439     
10440     /**
10441      * Strips all HTML tags to sort on text only - Case insensitive
10442      * @param {Mixed} s The value being converted
10443      * @return {String} The comparison value
10444      */
10445     asUCText : function(s){
10446         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10447     },
10448     
10449     /**
10450      * Case insensitive string
10451      * @param {Mixed} s The value being converted
10452      * @return {String} The comparison value
10453      */
10454     asUCString : function(s) {
10455         return String(s).toUpperCase();
10456     },
10457     
10458     /**
10459      * Date sorting
10460      * @param {Mixed} s The value being converted
10461      * @return {Number} The comparison value
10462      */
10463     asDate : function(s) {
10464         if(!s){
10465             return 0;
10466         }
10467         if(s instanceof Date){
10468             return s.getTime();
10469         }
10470         return Date.parse(String(s));
10471     },
10472     
10473     /**
10474      * Float sorting
10475      * @param {Mixed} s The value being converted
10476      * @return {Float} The comparison value
10477      */
10478     asFloat : function(s) {
10479         var val = parseFloat(String(s).replace(/,/g, ""));
10480         if(isNaN(val)) {
10481             val = 0;
10482         }
10483         return val;
10484     },
10485     
10486     /**
10487      * Integer sorting
10488      * @param {Mixed} s The value being converted
10489      * @return {Number} The comparison value
10490      */
10491     asInt : function(s) {
10492         var val = parseInt(String(s).replace(/,/g, ""));
10493         if(isNaN(val)) {
10494             val = 0;
10495         }
10496         return val;
10497     }
10498 };/*
10499  * Based on:
10500  * Ext JS Library 1.1.1
10501  * Copyright(c) 2006-2007, Ext JS, LLC.
10502  *
10503  * Originally Released Under LGPL - original licence link has changed is not relivant.
10504  *
10505  * Fork - LGPL
10506  * <script type="text/javascript">
10507  */
10508
10509 /**
10510 * @class Roo.data.Record
10511  * Instances of this class encapsulate both record <em>definition</em> information, and record
10512  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10513  * to access Records cached in an {@link Roo.data.Store} object.<br>
10514  * <p>
10515  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10516  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10517  * objects.<br>
10518  * <p>
10519  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10520  * @constructor
10521  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10522  * {@link #create}. The parameters are the same.
10523  * @param {Array} data An associative Array of data values keyed by the field name.
10524  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10525  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10526  * not specified an integer id is generated.
10527  */
10528 Roo.data.Record = function(data, id){
10529     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10530     this.data = data;
10531 };
10532
10533 /**
10534  * Generate a constructor for a specific record layout.
10535  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10536  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10537  * Each field definition object may contain the following properties: <ul>
10538  * <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,
10539  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10540  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10541  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10542  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10543  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10544  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10545  * this may be omitted.</p></li>
10546  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10547  * <ul><li>auto (Default, implies no conversion)</li>
10548  * <li>string</li>
10549  * <li>int</li>
10550  * <li>float</li>
10551  * <li>boolean</li>
10552  * <li>date</li></ul></p></li>
10553  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10554  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10555  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10556  * by the Reader into an object that will be stored in the Record. It is passed the
10557  * following parameters:<ul>
10558  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10559  * </ul></p></li>
10560  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10561  * </ul>
10562  * <br>usage:<br><pre><code>
10563 var TopicRecord = Roo.data.Record.create(
10564     {name: 'title', mapping: 'topic_title'},
10565     {name: 'author', mapping: 'username'},
10566     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10567     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10568     {name: 'lastPoster', mapping: 'user2'},
10569     {name: 'excerpt', mapping: 'post_text'}
10570 );
10571
10572 var myNewRecord = new TopicRecord({
10573     title: 'Do my job please',
10574     author: 'noobie',
10575     totalPosts: 1,
10576     lastPost: new Date(),
10577     lastPoster: 'Animal',
10578     excerpt: 'No way dude!'
10579 });
10580 myStore.add(myNewRecord);
10581 </code></pre>
10582  * @method create
10583  * @static
10584  */
10585 Roo.data.Record.create = function(o){
10586     var f = function(){
10587         f.superclass.constructor.apply(this, arguments);
10588     };
10589     Roo.extend(f, Roo.data.Record);
10590     var p = f.prototype;
10591     p.fields = new Roo.util.MixedCollection(false, function(field){
10592         return field.name;
10593     });
10594     for(var i = 0, len = o.length; i < len; i++){
10595         p.fields.add(new Roo.data.Field(o[i]));
10596     }
10597     f.getField = function(name){
10598         return p.fields.get(name);  
10599     };
10600     return f;
10601 };
10602
10603 Roo.data.Record.AUTO_ID = 1000;
10604 Roo.data.Record.EDIT = 'edit';
10605 Roo.data.Record.REJECT = 'reject';
10606 Roo.data.Record.COMMIT = 'commit';
10607
10608 Roo.data.Record.prototype = {
10609     /**
10610      * Readonly flag - true if this record has been modified.
10611      * @type Boolean
10612      */
10613     dirty : false,
10614     editing : false,
10615     error: null,
10616     modified: null,
10617
10618     // private
10619     join : function(store){
10620         this.store = store;
10621     },
10622
10623     /**
10624      * Set the named field to the specified value.
10625      * @param {String} name The name of the field to set.
10626      * @param {Object} value The value to set the field to.
10627      */
10628     set : function(name, value){
10629         if(this.data[name] == value){
10630             return;
10631         }
10632         this.dirty = true;
10633         if(!this.modified){
10634             this.modified = {};
10635         }
10636         if(typeof this.modified[name] == 'undefined'){
10637             this.modified[name] = this.data[name];
10638         }
10639         this.data[name] = value;
10640         if(!this.editing && this.store){
10641             this.store.afterEdit(this);
10642         }       
10643     },
10644
10645     /**
10646      * Get the value of the named field.
10647      * @param {String} name The name of the field to get the value of.
10648      * @return {Object} The value of the field.
10649      */
10650     get : function(name){
10651         return this.data[name]; 
10652     },
10653
10654     // private
10655     beginEdit : function(){
10656         this.editing = true;
10657         this.modified = {}; 
10658     },
10659
10660     // private
10661     cancelEdit : function(){
10662         this.editing = false;
10663         delete this.modified;
10664     },
10665
10666     // private
10667     endEdit : function(){
10668         this.editing = false;
10669         if(this.dirty && this.store){
10670             this.store.afterEdit(this);
10671         }
10672     },
10673
10674     /**
10675      * Usually called by the {@link Roo.data.Store} which owns the Record.
10676      * Rejects all changes made to the Record since either creation, or the last commit operation.
10677      * Modified fields are reverted to their original values.
10678      * <p>
10679      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10680      * of reject operations.
10681      */
10682     reject : function(){
10683         var m = this.modified;
10684         for(var n in m){
10685             if(typeof m[n] != "function"){
10686                 this.data[n] = m[n];
10687             }
10688         }
10689         this.dirty = false;
10690         delete this.modified;
10691         this.editing = false;
10692         if(this.store){
10693             this.store.afterReject(this);
10694         }
10695     },
10696
10697     /**
10698      * Usually called by the {@link Roo.data.Store} which owns the Record.
10699      * Commits all changes made to the Record since either creation, or the last commit operation.
10700      * <p>
10701      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10702      * of commit operations.
10703      */
10704     commit : function(){
10705         this.dirty = false;
10706         delete this.modified;
10707         this.editing = false;
10708         if(this.store){
10709             this.store.afterCommit(this);
10710         }
10711     },
10712
10713     // private
10714     hasError : function(){
10715         return this.error != null;
10716     },
10717
10718     // private
10719     clearError : function(){
10720         this.error = null;
10721     },
10722
10723     /**
10724      * Creates a copy of this record.
10725      * @param {String} id (optional) A new record id if you don't want to use this record's id
10726      * @return {Record}
10727      */
10728     copy : function(newId) {
10729         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10730     }
10731 };/*
10732  * Based on:
10733  * Ext JS Library 1.1.1
10734  * Copyright(c) 2006-2007, Ext JS, LLC.
10735  *
10736  * Originally Released Under LGPL - original licence link has changed is not relivant.
10737  *
10738  * Fork - LGPL
10739  * <script type="text/javascript">
10740  */
10741
10742
10743
10744 /**
10745  * @class Roo.data.Store
10746  * @extends Roo.util.Observable
10747  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10748  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10749  * <p>
10750  * 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
10751  * has no knowledge of the format of the data returned by the Proxy.<br>
10752  * <p>
10753  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10754  * instances from the data object. These records are cached and made available through accessor functions.
10755  * @constructor
10756  * Creates a new Store.
10757  * @param {Object} config A config object containing the objects needed for the Store to access data,
10758  * and read the data into Records.
10759  */
10760 Roo.data.Store = function(config){
10761     this.data = new Roo.util.MixedCollection(false);
10762     this.data.getKey = function(o){
10763         return o.id;
10764     };
10765     this.baseParams = {};
10766     // private
10767     this.paramNames = {
10768         "start" : "start",
10769         "limit" : "limit",
10770         "sort" : "sort",
10771         "dir" : "dir",
10772         "multisort" : "_multisort"
10773     };
10774
10775     if(config && config.data){
10776         this.inlineData = config.data;
10777         delete config.data;
10778     }
10779
10780     Roo.apply(this, config);
10781     
10782     if(this.reader){ // reader passed
10783         this.reader = Roo.factory(this.reader, Roo.data);
10784         this.reader.xmodule = this.xmodule || false;
10785         if(!this.recordType){
10786             this.recordType = this.reader.recordType;
10787         }
10788         if(this.reader.onMetaChange){
10789             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10790         }
10791     }
10792
10793     if(this.recordType){
10794         this.fields = this.recordType.prototype.fields;
10795     }
10796     this.modified = [];
10797
10798     this.addEvents({
10799         /**
10800          * @event datachanged
10801          * Fires when the data cache has changed, and a widget which is using this Store
10802          * as a Record cache should refresh its view.
10803          * @param {Store} this
10804          */
10805         datachanged : true,
10806         /**
10807          * @event metachange
10808          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10809          * @param {Store} this
10810          * @param {Object} meta The JSON metadata
10811          */
10812         metachange : true,
10813         /**
10814          * @event add
10815          * Fires when Records have been added to the Store
10816          * @param {Store} this
10817          * @param {Roo.data.Record[]} records The array of Records added
10818          * @param {Number} index The index at which the record(s) were added
10819          */
10820         add : true,
10821         /**
10822          * @event remove
10823          * Fires when a Record has been removed from the Store
10824          * @param {Store} this
10825          * @param {Roo.data.Record} record The Record that was removed
10826          * @param {Number} index The index at which the record was removed
10827          */
10828         remove : true,
10829         /**
10830          * @event update
10831          * Fires when a Record has been updated
10832          * @param {Store} this
10833          * @param {Roo.data.Record} record The Record that was updated
10834          * @param {String} operation The update operation being performed.  Value may be one of:
10835          * <pre><code>
10836  Roo.data.Record.EDIT
10837  Roo.data.Record.REJECT
10838  Roo.data.Record.COMMIT
10839          * </code></pre>
10840          */
10841         update : true,
10842         /**
10843          * @event clear
10844          * Fires when the data cache has been cleared.
10845          * @param {Store} this
10846          */
10847         clear : true,
10848         /**
10849          * @event beforeload
10850          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10851          * the load action will be canceled.
10852          * @param {Store} this
10853          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10854          */
10855         beforeload : true,
10856         /**
10857          * @event beforeloadadd
10858          * Fires after a new set of Records has been loaded.
10859          * @param {Store} this
10860          * @param {Roo.data.Record[]} records The Records that were loaded
10861          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10862          */
10863         beforeloadadd : true,
10864         /**
10865          * @event load
10866          * Fires after a new set of Records has been loaded, before they are added to the store.
10867          * @param {Store} this
10868          * @param {Roo.data.Record[]} records The Records that were loaded
10869          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10870          * @params {Object} return from reader
10871          */
10872         load : true,
10873         /**
10874          * @event loadexception
10875          * Fires if an exception occurs in the Proxy during loading.
10876          * Called with the signature of the Proxy's "loadexception" event.
10877          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10878          * 
10879          * @param {Proxy} 
10880          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10881          * @param {Object} load options 
10882          * @param {Object} jsonData from your request (normally this contains the Exception)
10883          */
10884         loadexception : true
10885     });
10886     
10887     if(this.proxy){
10888         this.proxy = Roo.factory(this.proxy, Roo.data);
10889         this.proxy.xmodule = this.xmodule || false;
10890         this.relayEvents(this.proxy,  ["loadexception"]);
10891     }
10892     this.sortToggle = {};
10893     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10894
10895     Roo.data.Store.superclass.constructor.call(this);
10896
10897     if(this.inlineData){
10898         this.loadData(this.inlineData);
10899         delete this.inlineData;
10900     }
10901 };
10902
10903 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10904      /**
10905     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10906     * without a remote query - used by combo/forms at present.
10907     */
10908     
10909     /**
10910     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10911     */
10912     /**
10913     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10914     */
10915     /**
10916     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10917     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10918     */
10919     /**
10920     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10921     * on any HTTP request
10922     */
10923     /**
10924     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10925     */
10926     /**
10927     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10928     */
10929     multiSort: false,
10930     /**
10931     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10932     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10933     */
10934     remoteSort : false,
10935
10936     /**
10937     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10938      * loaded or when a record is removed. (defaults to false).
10939     */
10940     pruneModifiedRecords : false,
10941
10942     // private
10943     lastOptions : null,
10944
10945     /**
10946      * Add Records to the Store and fires the add event.
10947      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10948      */
10949     add : function(records){
10950         records = [].concat(records);
10951         for(var i = 0, len = records.length; i < len; i++){
10952             records[i].join(this);
10953         }
10954         var index = this.data.length;
10955         this.data.addAll(records);
10956         this.fireEvent("add", this, records, index);
10957     },
10958
10959     /**
10960      * Remove a Record from the Store and fires the remove event.
10961      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10962      */
10963     remove : function(record){
10964         var index = this.data.indexOf(record);
10965         this.data.removeAt(index);
10966         if(this.pruneModifiedRecords){
10967             this.modified.remove(record);
10968         }
10969         this.fireEvent("remove", this, record, index);
10970     },
10971
10972     /**
10973      * Remove all Records from the Store and fires the clear event.
10974      */
10975     removeAll : function(){
10976         this.data.clear();
10977         if(this.pruneModifiedRecords){
10978             this.modified = [];
10979         }
10980         this.fireEvent("clear", this);
10981     },
10982
10983     /**
10984      * Inserts Records to the Store at the given index and fires the add event.
10985      * @param {Number} index The start index at which to insert the passed Records.
10986      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10987      */
10988     insert : function(index, records){
10989         records = [].concat(records);
10990         for(var i = 0, len = records.length; i < len; i++){
10991             this.data.insert(index, records[i]);
10992             records[i].join(this);
10993         }
10994         this.fireEvent("add", this, records, index);
10995     },
10996
10997     /**
10998      * Get the index within the cache of the passed Record.
10999      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11000      * @return {Number} The index of the passed Record. Returns -1 if not found.
11001      */
11002     indexOf : function(record){
11003         return this.data.indexOf(record);
11004     },
11005
11006     /**
11007      * Get the index within the cache of the Record with the passed id.
11008      * @param {String} id The id of the Record to find.
11009      * @return {Number} The index of the Record. Returns -1 if not found.
11010      */
11011     indexOfId : function(id){
11012         return this.data.indexOfKey(id);
11013     },
11014
11015     /**
11016      * Get the Record with the specified id.
11017      * @param {String} id The id of the Record to find.
11018      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11019      */
11020     getById : function(id){
11021         return this.data.key(id);
11022     },
11023
11024     /**
11025      * Get the Record at the specified index.
11026      * @param {Number} index The index of the Record to find.
11027      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11028      */
11029     getAt : function(index){
11030         return this.data.itemAt(index);
11031     },
11032
11033     /**
11034      * Returns a range of Records between specified indices.
11035      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11036      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11037      * @return {Roo.data.Record[]} An array of Records
11038      */
11039     getRange : function(start, end){
11040         return this.data.getRange(start, end);
11041     },
11042
11043     // private
11044     storeOptions : function(o){
11045         o = Roo.apply({}, o);
11046         delete o.callback;
11047         delete o.scope;
11048         this.lastOptions = o;
11049     },
11050
11051     /**
11052      * Loads the Record cache from the configured Proxy using the configured Reader.
11053      * <p>
11054      * If using remote paging, then the first load call must specify the <em>start</em>
11055      * and <em>limit</em> properties in the options.params property to establish the initial
11056      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11057      * <p>
11058      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11059      * and this call will return before the new data has been loaded. Perform any post-processing
11060      * in a callback function, or in a "load" event handler.</strong>
11061      * <p>
11062      * @param {Object} options An object containing properties which control loading options:<ul>
11063      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11064      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11065      * passed the following arguments:<ul>
11066      * <li>r : Roo.data.Record[]</li>
11067      * <li>options: Options object from the load call</li>
11068      * <li>success: Boolean success indicator</li></ul></li>
11069      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11070      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11071      * </ul>
11072      */
11073     load : function(options){
11074         options = options || {};
11075         if(this.fireEvent("beforeload", this, options) !== false){
11076             this.storeOptions(options);
11077             var p = Roo.apply(options.params || {}, this.baseParams);
11078             // if meta was not loaded from remote source.. try requesting it.
11079             if (!this.reader.metaFromRemote) {
11080                 p._requestMeta = 1;
11081             }
11082             if(this.sortInfo && this.remoteSort){
11083                 var pn = this.paramNames;
11084                 p[pn["sort"]] = this.sortInfo.field;
11085                 p[pn["dir"]] = this.sortInfo.direction;
11086             }
11087             if (this.multiSort) {
11088                 var pn = this.paramNames;
11089                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11090             }
11091             
11092             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11093         }
11094     },
11095
11096     /**
11097      * Reloads the Record cache from the configured Proxy using the configured Reader and
11098      * the options from the last load operation performed.
11099      * @param {Object} options (optional) An object containing properties which may override the options
11100      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11101      * the most recently used options are reused).
11102      */
11103     reload : function(options){
11104         this.load(Roo.applyIf(options||{}, this.lastOptions));
11105     },
11106
11107     // private
11108     // Called as a callback by the Reader during a load operation.
11109     loadRecords : function(o, options, success){
11110         if(!o || success === false){
11111             if(success !== false){
11112                 this.fireEvent("load", this, [], options, o);
11113             }
11114             if(options.callback){
11115                 options.callback.call(options.scope || this, [], options, false);
11116             }
11117             return;
11118         }
11119         // if data returned failure - throw an exception.
11120         if (o.success === false) {
11121             // show a message if no listener is registered.
11122             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11123                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11124             }
11125             // loadmask wil be hooked into this..
11126             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11127             return;
11128         }
11129         var r = o.records, t = o.totalRecords || r.length;
11130         
11131         this.fireEvent("beforeloadadd", this, r, options, o);
11132         
11133         if(!options || options.add !== true){
11134             if(this.pruneModifiedRecords){
11135                 this.modified = [];
11136             }
11137             for(var i = 0, len = r.length; i < len; i++){
11138                 r[i].join(this);
11139             }
11140             if(this.snapshot){
11141                 this.data = this.snapshot;
11142                 delete this.snapshot;
11143             }
11144             this.data.clear();
11145             this.data.addAll(r);
11146             this.totalLength = t;
11147             this.applySort();
11148             this.fireEvent("datachanged", this);
11149         }else{
11150             this.totalLength = Math.max(t, this.data.length+r.length);
11151             this.add(r);
11152         }
11153         
11154         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11155                 
11156             var e = new Roo.data.Record({});
11157
11158             e.set(this.parent.displayField, this.parent.emptyTitle);
11159             e.set(this.parent.valueField, '');
11160
11161             this.insert(0, e);
11162         }
11163             
11164         this.fireEvent("load", this, r, options, o);
11165         if(options.callback){
11166             options.callback.call(options.scope || this, r, options, true);
11167         }
11168     },
11169
11170
11171     /**
11172      * Loads data from a passed data block. A Reader which understands the format of the data
11173      * must have been configured in the constructor.
11174      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11175      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11176      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11177      */
11178     loadData : function(o, append){
11179         var r = this.reader.readRecords(o);
11180         this.loadRecords(r, {add: append}, true);
11181     },
11182
11183     /**
11184      * Gets the number of cached records.
11185      * <p>
11186      * <em>If using paging, this may not be the total size of the dataset. If the data object
11187      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11188      * the data set size</em>
11189      */
11190     getCount : function(){
11191         return this.data.length || 0;
11192     },
11193
11194     /**
11195      * Gets the total number of records in the dataset as returned by the server.
11196      * <p>
11197      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11198      * the dataset size</em>
11199      */
11200     getTotalCount : function(){
11201         return this.totalLength || 0;
11202     },
11203
11204     /**
11205      * Returns the sort state of the Store as an object with two properties:
11206      * <pre><code>
11207  field {String} The name of the field by which the Records are sorted
11208  direction {String} The sort order, "ASC" or "DESC"
11209      * </code></pre>
11210      */
11211     getSortState : function(){
11212         return this.sortInfo;
11213     },
11214
11215     // private
11216     applySort : function(){
11217         if(this.sortInfo && !this.remoteSort){
11218             var s = this.sortInfo, f = s.field;
11219             var st = this.fields.get(f).sortType;
11220             var fn = function(r1, r2){
11221                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11222                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11223             };
11224             this.data.sort(s.direction, fn);
11225             if(this.snapshot && this.snapshot != this.data){
11226                 this.snapshot.sort(s.direction, fn);
11227             }
11228         }
11229     },
11230
11231     /**
11232      * Sets the default sort column and order to be used by the next load operation.
11233      * @param {String} fieldName The name of the field to sort by.
11234      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11235      */
11236     setDefaultSort : function(field, dir){
11237         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11238     },
11239
11240     /**
11241      * Sort the Records.
11242      * If remote sorting is used, the sort is performed on the server, and the cache is
11243      * reloaded. If local sorting is used, the cache is sorted internally.
11244      * @param {String} fieldName The name of the field to sort by.
11245      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11246      */
11247     sort : function(fieldName, dir){
11248         var f = this.fields.get(fieldName);
11249         if(!dir){
11250             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11251             
11252             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11253                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11254             }else{
11255                 dir = f.sortDir;
11256             }
11257         }
11258         this.sortToggle[f.name] = dir;
11259         this.sortInfo = {field: f.name, direction: dir};
11260         if(!this.remoteSort){
11261             this.applySort();
11262             this.fireEvent("datachanged", this);
11263         }else{
11264             this.load(this.lastOptions);
11265         }
11266     },
11267
11268     /**
11269      * Calls the specified function for each of the Records in the cache.
11270      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11271      * Returning <em>false</em> aborts and exits the iteration.
11272      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11273      */
11274     each : function(fn, scope){
11275         this.data.each(fn, scope);
11276     },
11277
11278     /**
11279      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11280      * (e.g., during paging).
11281      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11282      */
11283     getModifiedRecords : function(){
11284         return this.modified;
11285     },
11286
11287     // private
11288     createFilterFn : function(property, value, anyMatch){
11289         if(!value.exec){ // not a regex
11290             value = String(value);
11291             if(value.length == 0){
11292                 return false;
11293             }
11294             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11295         }
11296         return function(r){
11297             return value.test(r.data[property]);
11298         };
11299     },
11300
11301     /**
11302      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11303      * @param {String} property A field on your records
11304      * @param {Number} start The record index to start at (defaults to 0)
11305      * @param {Number} end The last record index to include (defaults to length - 1)
11306      * @return {Number} The sum
11307      */
11308     sum : function(property, start, end){
11309         var rs = this.data.items, v = 0;
11310         start = start || 0;
11311         end = (end || end === 0) ? end : rs.length-1;
11312
11313         for(var i = start; i <= end; i++){
11314             v += (rs[i].data[property] || 0);
11315         }
11316         return v;
11317     },
11318
11319     /**
11320      * Filter the records by a specified property.
11321      * @param {String} field A field on your records
11322      * @param {String/RegExp} value Either a string that the field
11323      * should start with or a RegExp to test against the field
11324      * @param {Boolean} anyMatch True to match any part not just the beginning
11325      */
11326     filter : function(property, value, anyMatch){
11327         var fn = this.createFilterFn(property, value, anyMatch);
11328         return fn ? this.filterBy(fn) : this.clearFilter();
11329     },
11330
11331     /**
11332      * Filter by a function. The specified function will be called with each
11333      * record in this data source. If the function returns true the record is included,
11334      * otherwise it is filtered.
11335      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11336      * @param {Object} scope (optional) The scope of the function (defaults to this)
11337      */
11338     filterBy : function(fn, scope){
11339         this.snapshot = this.snapshot || this.data;
11340         this.data = this.queryBy(fn, scope||this);
11341         this.fireEvent("datachanged", this);
11342     },
11343
11344     /**
11345      * Query the records by a specified property.
11346      * @param {String} field A field on your records
11347      * @param {String/RegExp} value Either a string that the field
11348      * should start with or a RegExp to test against the field
11349      * @param {Boolean} anyMatch True to match any part not just the beginning
11350      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11351      */
11352     query : function(property, value, anyMatch){
11353         var fn = this.createFilterFn(property, value, anyMatch);
11354         return fn ? this.queryBy(fn) : this.data.clone();
11355     },
11356
11357     /**
11358      * Query by a function. The specified function will be called with each
11359      * record in this data source. If the function returns true the record is included
11360      * in the results.
11361      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11362      * @param {Object} scope (optional) The scope of the function (defaults to this)
11363       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11364      **/
11365     queryBy : function(fn, scope){
11366         var data = this.snapshot || this.data;
11367         return data.filterBy(fn, scope||this);
11368     },
11369
11370     /**
11371      * Collects unique values for a particular dataIndex from this store.
11372      * @param {String} dataIndex The property to collect
11373      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11374      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11375      * @return {Array} An array of the unique values
11376      **/
11377     collect : function(dataIndex, allowNull, bypassFilter){
11378         var d = (bypassFilter === true && this.snapshot) ?
11379                 this.snapshot.items : this.data.items;
11380         var v, sv, r = [], l = {};
11381         for(var i = 0, len = d.length; i < len; i++){
11382             v = d[i].data[dataIndex];
11383             sv = String(v);
11384             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11385                 l[sv] = true;
11386                 r[r.length] = v;
11387             }
11388         }
11389         return r;
11390     },
11391
11392     /**
11393      * Revert to a view of the Record cache with no filtering applied.
11394      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11395      */
11396     clearFilter : function(suppressEvent){
11397         if(this.snapshot && this.snapshot != this.data){
11398             this.data = this.snapshot;
11399             delete this.snapshot;
11400             if(suppressEvent !== true){
11401                 this.fireEvent("datachanged", this);
11402             }
11403         }
11404     },
11405
11406     // private
11407     afterEdit : function(record){
11408         if(this.modified.indexOf(record) == -1){
11409             this.modified.push(record);
11410         }
11411         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11412     },
11413     
11414     // private
11415     afterReject : function(record){
11416         this.modified.remove(record);
11417         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11418     },
11419
11420     // private
11421     afterCommit : function(record){
11422         this.modified.remove(record);
11423         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11424     },
11425
11426     /**
11427      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11428      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11429      */
11430     commitChanges : function(){
11431         var m = this.modified.slice(0);
11432         this.modified = [];
11433         for(var i = 0, len = m.length; i < len; i++){
11434             m[i].commit();
11435         }
11436     },
11437
11438     /**
11439      * Cancel outstanding changes on all changed records.
11440      */
11441     rejectChanges : function(){
11442         var m = this.modified.slice(0);
11443         this.modified = [];
11444         for(var i = 0, len = m.length; i < len; i++){
11445             m[i].reject();
11446         }
11447     },
11448
11449     onMetaChange : function(meta, rtype, o){
11450         this.recordType = rtype;
11451         this.fields = rtype.prototype.fields;
11452         delete this.snapshot;
11453         this.sortInfo = meta.sortInfo || this.sortInfo;
11454         this.modified = [];
11455         this.fireEvent('metachange', this, this.reader.meta);
11456     },
11457     
11458     moveIndex : function(data, type)
11459     {
11460         var index = this.indexOf(data);
11461         
11462         var newIndex = index + type;
11463         
11464         this.remove(data);
11465         
11466         this.insert(newIndex, data);
11467         
11468     }
11469 });/*
11470  * Based on:
11471  * Ext JS Library 1.1.1
11472  * Copyright(c) 2006-2007, Ext JS, LLC.
11473  *
11474  * Originally Released Under LGPL - original licence link has changed is not relivant.
11475  *
11476  * Fork - LGPL
11477  * <script type="text/javascript">
11478  */
11479
11480 /**
11481  * @class Roo.data.SimpleStore
11482  * @extends Roo.data.Store
11483  * Small helper class to make creating Stores from Array data easier.
11484  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11485  * @cfg {Array} fields An array of field definition objects, or field name strings.
11486  * @cfg {Array} data The multi-dimensional array of data
11487  * @constructor
11488  * @param {Object} config
11489  */
11490 Roo.data.SimpleStore = function(config){
11491     Roo.data.SimpleStore.superclass.constructor.call(this, {
11492         isLocal : true,
11493         reader: new Roo.data.ArrayReader({
11494                 id: config.id
11495             },
11496             Roo.data.Record.create(config.fields)
11497         ),
11498         proxy : new Roo.data.MemoryProxy(config.data)
11499     });
11500     this.load();
11501 };
11502 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11503  * Based on:
11504  * Ext JS Library 1.1.1
11505  * Copyright(c) 2006-2007, Ext JS, LLC.
11506  *
11507  * Originally Released Under LGPL - original licence link has changed is not relivant.
11508  *
11509  * Fork - LGPL
11510  * <script type="text/javascript">
11511  */
11512
11513 /**
11514 /**
11515  * @extends Roo.data.Store
11516  * @class Roo.data.JsonStore
11517  * Small helper class to make creating Stores for JSON data easier. <br/>
11518 <pre><code>
11519 var store = new Roo.data.JsonStore({
11520     url: 'get-images.php',
11521     root: 'images',
11522     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11523 });
11524 </code></pre>
11525  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11526  * JsonReader and HttpProxy (unless inline data is provided).</b>
11527  * @cfg {Array} fields An array of field definition objects, or field name strings.
11528  * @constructor
11529  * @param {Object} config
11530  */
11531 Roo.data.JsonStore = function(c){
11532     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11533         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11534         reader: new Roo.data.JsonReader(c, c.fields)
11535     }));
11536 };
11537 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11538  * Based on:
11539  * Ext JS Library 1.1.1
11540  * Copyright(c) 2006-2007, Ext JS, LLC.
11541  *
11542  * Originally Released Under LGPL - original licence link has changed is not relivant.
11543  *
11544  * Fork - LGPL
11545  * <script type="text/javascript">
11546  */
11547
11548  
11549 Roo.data.Field = function(config){
11550     if(typeof config == "string"){
11551         config = {name: config};
11552     }
11553     Roo.apply(this, config);
11554     
11555     if(!this.type){
11556         this.type = "auto";
11557     }
11558     
11559     var st = Roo.data.SortTypes;
11560     // named sortTypes are supported, here we look them up
11561     if(typeof this.sortType == "string"){
11562         this.sortType = st[this.sortType];
11563     }
11564     
11565     // set default sortType for strings and dates
11566     if(!this.sortType){
11567         switch(this.type){
11568             case "string":
11569                 this.sortType = st.asUCString;
11570                 break;
11571             case "date":
11572                 this.sortType = st.asDate;
11573                 break;
11574             default:
11575                 this.sortType = st.none;
11576         }
11577     }
11578
11579     // define once
11580     var stripRe = /[\$,%]/g;
11581
11582     // prebuilt conversion function for this field, instead of
11583     // switching every time we're reading a value
11584     if(!this.convert){
11585         var cv, dateFormat = this.dateFormat;
11586         switch(this.type){
11587             case "":
11588             case "auto":
11589             case undefined:
11590                 cv = function(v){ return v; };
11591                 break;
11592             case "string":
11593                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11594                 break;
11595             case "int":
11596                 cv = function(v){
11597                     return v !== undefined && v !== null && v !== '' ?
11598                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11599                     };
11600                 break;
11601             case "float":
11602                 cv = function(v){
11603                     return v !== undefined && v !== null && v !== '' ?
11604                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11605                     };
11606                 break;
11607             case "bool":
11608             case "boolean":
11609                 cv = function(v){ return v === true || v === "true" || v == 1; };
11610                 break;
11611             case "date":
11612                 cv = function(v){
11613                     if(!v){
11614                         return '';
11615                     }
11616                     if(v instanceof Date){
11617                         return v;
11618                     }
11619                     if(dateFormat){
11620                         if(dateFormat == "timestamp"){
11621                             return new Date(v*1000);
11622                         }
11623                         return Date.parseDate(v, dateFormat);
11624                     }
11625                     var parsed = Date.parse(v);
11626                     return parsed ? new Date(parsed) : null;
11627                 };
11628              break;
11629             
11630         }
11631         this.convert = cv;
11632     }
11633 };
11634
11635 Roo.data.Field.prototype = {
11636     dateFormat: null,
11637     defaultValue: "",
11638     mapping: null,
11639     sortType : null,
11640     sortDir : "ASC"
11641 };/*
11642  * Based on:
11643  * Ext JS Library 1.1.1
11644  * Copyright(c) 2006-2007, Ext JS, LLC.
11645  *
11646  * Originally Released Under LGPL - original licence link has changed is not relivant.
11647  *
11648  * Fork - LGPL
11649  * <script type="text/javascript">
11650  */
11651  
11652 // Base class for reading structured data from a data source.  This class is intended to be
11653 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11654
11655 /**
11656  * @class Roo.data.DataReader
11657  * Base class for reading structured data from a data source.  This class is intended to be
11658  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11659  */
11660
11661 Roo.data.DataReader = function(meta, recordType){
11662     
11663     this.meta = meta;
11664     
11665     this.recordType = recordType instanceof Array ? 
11666         Roo.data.Record.create(recordType) : recordType;
11667 };
11668
11669 Roo.data.DataReader.prototype = {
11670      /**
11671      * Create an empty record
11672      * @param {Object} data (optional) - overlay some values
11673      * @return {Roo.data.Record} record created.
11674      */
11675     newRow :  function(d) {
11676         var da =  {};
11677         this.recordType.prototype.fields.each(function(c) {
11678             switch( c.type) {
11679                 case 'int' : da[c.name] = 0; break;
11680                 case 'date' : da[c.name] = new Date(); break;
11681                 case 'float' : da[c.name] = 0.0; break;
11682                 case 'boolean' : da[c.name] = false; break;
11683                 default : da[c.name] = ""; break;
11684             }
11685             
11686         });
11687         return new this.recordType(Roo.apply(da, d));
11688     }
11689     
11690 };/*
11691  * Based on:
11692  * Ext JS Library 1.1.1
11693  * Copyright(c) 2006-2007, Ext JS, LLC.
11694  *
11695  * Originally Released Under LGPL - original licence link has changed is not relivant.
11696  *
11697  * Fork - LGPL
11698  * <script type="text/javascript">
11699  */
11700
11701 /**
11702  * @class Roo.data.DataProxy
11703  * @extends Roo.data.Observable
11704  * This class is an abstract base class for implementations which provide retrieval of
11705  * unformatted data objects.<br>
11706  * <p>
11707  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11708  * (of the appropriate type which knows how to parse the data object) to provide a block of
11709  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11710  * <p>
11711  * Custom implementations must implement the load method as described in
11712  * {@link Roo.data.HttpProxy#load}.
11713  */
11714 Roo.data.DataProxy = function(){
11715     this.addEvents({
11716         /**
11717          * @event beforeload
11718          * Fires before a network request is made to retrieve a data object.
11719          * @param {Object} This DataProxy object.
11720          * @param {Object} params The params parameter to the load function.
11721          */
11722         beforeload : true,
11723         /**
11724          * @event load
11725          * Fires before the load method's callback is called.
11726          * @param {Object} This DataProxy object.
11727          * @param {Object} o The data object.
11728          * @param {Object} arg The callback argument object passed to the load function.
11729          */
11730         load : true,
11731         /**
11732          * @event loadexception
11733          * Fires if an Exception occurs during data retrieval.
11734          * @param {Object} This DataProxy object.
11735          * @param {Object} o The data object.
11736          * @param {Object} arg The callback argument object passed to the load function.
11737          * @param {Object} e The Exception.
11738          */
11739         loadexception : true
11740     });
11741     Roo.data.DataProxy.superclass.constructor.call(this);
11742 };
11743
11744 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11745
11746     /**
11747      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11748      */
11749 /*
11750  * Based on:
11751  * Ext JS Library 1.1.1
11752  * Copyright(c) 2006-2007, Ext JS, LLC.
11753  *
11754  * Originally Released Under LGPL - original licence link has changed is not relivant.
11755  *
11756  * Fork - LGPL
11757  * <script type="text/javascript">
11758  */
11759 /**
11760  * @class Roo.data.MemoryProxy
11761  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11762  * to the Reader when its load method is called.
11763  * @constructor
11764  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11765  */
11766 Roo.data.MemoryProxy = function(data){
11767     if (data.data) {
11768         data = data.data;
11769     }
11770     Roo.data.MemoryProxy.superclass.constructor.call(this);
11771     this.data = data;
11772 };
11773
11774 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11775     
11776     /**
11777      * Load data from the requested source (in this case an in-memory
11778      * data object passed to the constructor), read the data object into
11779      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11780      * process that block using the passed callback.
11781      * @param {Object} params This parameter is not used by the MemoryProxy class.
11782      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11783      * object into a block of Roo.data.Records.
11784      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11785      * The function must be passed <ul>
11786      * <li>The Record block object</li>
11787      * <li>The "arg" argument from the load function</li>
11788      * <li>A boolean success indicator</li>
11789      * </ul>
11790      * @param {Object} scope The scope in which to call the callback
11791      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11792      */
11793     load : function(params, reader, callback, scope, arg){
11794         params = params || {};
11795         var result;
11796         try {
11797             result = reader.readRecords(this.data);
11798         }catch(e){
11799             this.fireEvent("loadexception", this, arg, null, e);
11800             callback.call(scope, null, arg, false);
11801             return;
11802         }
11803         callback.call(scope, result, arg, true);
11804     },
11805     
11806     // private
11807     update : function(params, records){
11808         
11809     }
11810 });/*
11811  * Based on:
11812  * Ext JS Library 1.1.1
11813  * Copyright(c) 2006-2007, Ext JS, LLC.
11814  *
11815  * Originally Released Under LGPL - original licence link has changed is not relivant.
11816  *
11817  * Fork - LGPL
11818  * <script type="text/javascript">
11819  */
11820 /**
11821  * @class Roo.data.HttpProxy
11822  * @extends Roo.data.DataProxy
11823  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11824  * configured to reference a certain URL.<br><br>
11825  * <p>
11826  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11827  * from which the running page was served.<br><br>
11828  * <p>
11829  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11830  * <p>
11831  * Be aware that to enable the browser to parse an XML document, the server must set
11832  * the Content-Type header in the HTTP response to "text/xml".
11833  * @constructor
11834  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11835  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11836  * will be used to make the request.
11837  */
11838 Roo.data.HttpProxy = function(conn){
11839     Roo.data.HttpProxy.superclass.constructor.call(this);
11840     // is conn a conn config or a real conn?
11841     this.conn = conn;
11842     this.useAjax = !conn || !conn.events;
11843   
11844 };
11845
11846 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11847     // thse are take from connection...
11848     
11849     /**
11850      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11851      */
11852     /**
11853      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11854      * extra parameters to each request made by this object. (defaults to undefined)
11855      */
11856     /**
11857      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11858      *  to each request made by this object. (defaults to undefined)
11859      */
11860     /**
11861      * @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)
11862      */
11863     /**
11864      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11865      */
11866      /**
11867      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11868      * @type Boolean
11869      */
11870   
11871
11872     /**
11873      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11874      * @type Boolean
11875      */
11876     /**
11877      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11878      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11879      * a finer-grained basis than the DataProxy events.
11880      */
11881     getConnection : function(){
11882         return this.useAjax ? Roo.Ajax : this.conn;
11883     },
11884
11885     /**
11886      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11887      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11888      * process that block using the passed callback.
11889      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11890      * for the request to the remote server.
11891      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11892      * object into a block of Roo.data.Records.
11893      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11894      * The function must be passed <ul>
11895      * <li>The Record block object</li>
11896      * <li>The "arg" argument from the load function</li>
11897      * <li>A boolean success indicator</li>
11898      * </ul>
11899      * @param {Object} scope The scope in which to call the callback
11900      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11901      */
11902     load : function(params, reader, callback, scope, arg){
11903         if(this.fireEvent("beforeload", this, params) !== false){
11904             var  o = {
11905                 params : params || {},
11906                 request: {
11907                     callback : callback,
11908                     scope : scope,
11909                     arg : arg
11910                 },
11911                 reader: reader,
11912                 callback : this.loadResponse,
11913                 scope: this
11914             };
11915             if(this.useAjax){
11916                 Roo.applyIf(o, this.conn);
11917                 if(this.activeRequest){
11918                     Roo.Ajax.abort(this.activeRequest);
11919                 }
11920                 this.activeRequest = Roo.Ajax.request(o);
11921             }else{
11922                 this.conn.request(o);
11923             }
11924         }else{
11925             callback.call(scope||this, null, arg, false);
11926         }
11927     },
11928
11929     // private
11930     loadResponse : function(o, success, response){
11931         delete this.activeRequest;
11932         if(!success){
11933             this.fireEvent("loadexception", this, o, response);
11934             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11935             return;
11936         }
11937         var result;
11938         try {
11939             result = o.reader.read(response);
11940         }catch(e){
11941             this.fireEvent("loadexception", this, o, response, e);
11942             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11943             return;
11944         }
11945         
11946         this.fireEvent("load", this, o, o.request.arg);
11947         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11948     },
11949
11950     // private
11951     update : function(dataSet){
11952
11953     },
11954
11955     // private
11956     updateResponse : function(dataSet){
11957
11958     }
11959 });/*
11960  * Based on:
11961  * Ext JS Library 1.1.1
11962  * Copyright(c) 2006-2007, Ext JS, LLC.
11963  *
11964  * Originally Released Under LGPL - original licence link has changed is not relivant.
11965  *
11966  * Fork - LGPL
11967  * <script type="text/javascript">
11968  */
11969
11970 /**
11971  * @class Roo.data.ScriptTagProxy
11972  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11973  * other than the originating domain of the running page.<br><br>
11974  * <p>
11975  * <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
11976  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11977  * <p>
11978  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11979  * source code that is used as the source inside a &lt;script> tag.<br><br>
11980  * <p>
11981  * In order for the browser to process the returned data, the server must wrap the data object
11982  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11983  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11984  * depending on whether the callback name was passed:
11985  * <p>
11986  * <pre><code>
11987 boolean scriptTag = false;
11988 String cb = request.getParameter("callback");
11989 if (cb != null) {
11990     scriptTag = true;
11991     response.setContentType("text/javascript");
11992 } else {
11993     response.setContentType("application/x-json");
11994 }
11995 Writer out = response.getWriter();
11996 if (scriptTag) {
11997     out.write(cb + "(");
11998 }
11999 out.print(dataBlock.toJsonString());
12000 if (scriptTag) {
12001     out.write(");");
12002 }
12003 </pre></code>
12004  *
12005  * @constructor
12006  * @param {Object} config A configuration object.
12007  */
12008 Roo.data.ScriptTagProxy = function(config){
12009     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12010     Roo.apply(this, config);
12011     this.head = document.getElementsByTagName("head")[0];
12012 };
12013
12014 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12015
12016 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12017     /**
12018      * @cfg {String} url The URL from which to request the data object.
12019      */
12020     /**
12021      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12022      */
12023     timeout : 30000,
12024     /**
12025      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12026      * the server the name of the callback function set up by the load call to process the returned data object.
12027      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12028      * javascript output which calls this named function passing the data object as its only parameter.
12029      */
12030     callbackParam : "callback",
12031     /**
12032      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12033      * name to the request.
12034      */
12035     nocache : true,
12036
12037     /**
12038      * Load data from the configured URL, read the data object into
12039      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12040      * process that block using the passed callback.
12041      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12042      * for the request to the remote server.
12043      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12044      * object into a block of Roo.data.Records.
12045      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12046      * The function must be passed <ul>
12047      * <li>The Record block object</li>
12048      * <li>The "arg" argument from the load function</li>
12049      * <li>A boolean success indicator</li>
12050      * </ul>
12051      * @param {Object} scope The scope in which to call the callback
12052      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12053      */
12054     load : function(params, reader, callback, scope, arg){
12055         if(this.fireEvent("beforeload", this, params) !== false){
12056
12057             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12058
12059             var url = this.url;
12060             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12061             if(this.nocache){
12062                 url += "&_dc=" + (new Date().getTime());
12063             }
12064             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12065             var trans = {
12066                 id : transId,
12067                 cb : "stcCallback"+transId,
12068                 scriptId : "stcScript"+transId,
12069                 params : params,
12070                 arg : arg,
12071                 url : url,
12072                 callback : callback,
12073                 scope : scope,
12074                 reader : reader
12075             };
12076             var conn = this;
12077
12078             window[trans.cb] = function(o){
12079                 conn.handleResponse(o, trans);
12080             };
12081
12082             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12083
12084             if(this.autoAbort !== false){
12085                 this.abort();
12086             }
12087
12088             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12089
12090             var script = document.createElement("script");
12091             script.setAttribute("src", url);
12092             script.setAttribute("type", "text/javascript");
12093             script.setAttribute("id", trans.scriptId);
12094             this.head.appendChild(script);
12095
12096             this.trans = trans;
12097         }else{
12098             callback.call(scope||this, null, arg, false);
12099         }
12100     },
12101
12102     // private
12103     isLoading : function(){
12104         return this.trans ? true : false;
12105     },
12106
12107     /**
12108      * Abort the current server request.
12109      */
12110     abort : function(){
12111         if(this.isLoading()){
12112             this.destroyTrans(this.trans);
12113         }
12114     },
12115
12116     // private
12117     destroyTrans : function(trans, isLoaded){
12118         this.head.removeChild(document.getElementById(trans.scriptId));
12119         clearTimeout(trans.timeoutId);
12120         if(isLoaded){
12121             window[trans.cb] = undefined;
12122             try{
12123                 delete window[trans.cb];
12124             }catch(e){}
12125         }else{
12126             // if hasn't been loaded, wait for load to remove it to prevent script error
12127             window[trans.cb] = function(){
12128                 window[trans.cb] = undefined;
12129                 try{
12130                     delete window[trans.cb];
12131                 }catch(e){}
12132             };
12133         }
12134     },
12135
12136     // private
12137     handleResponse : function(o, trans){
12138         this.trans = false;
12139         this.destroyTrans(trans, true);
12140         var result;
12141         try {
12142             result = trans.reader.readRecords(o);
12143         }catch(e){
12144             this.fireEvent("loadexception", this, o, trans.arg, e);
12145             trans.callback.call(trans.scope||window, null, trans.arg, false);
12146             return;
12147         }
12148         this.fireEvent("load", this, o, trans.arg);
12149         trans.callback.call(trans.scope||window, result, trans.arg, true);
12150     },
12151
12152     // private
12153     handleFailure : function(trans){
12154         this.trans = false;
12155         this.destroyTrans(trans, false);
12156         this.fireEvent("loadexception", this, null, trans.arg);
12157         trans.callback.call(trans.scope||window, null, trans.arg, false);
12158     }
12159 });/*
12160  * Based on:
12161  * Ext JS Library 1.1.1
12162  * Copyright(c) 2006-2007, Ext JS, LLC.
12163  *
12164  * Originally Released Under LGPL - original licence link has changed is not relivant.
12165  *
12166  * Fork - LGPL
12167  * <script type="text/javascript">
12168  */
12169
12170 /**
12171  * @class Roo.data.JsonReader
12172  * @extends Roo.data.DataReader
12173  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12174  * based on mappings in a provided Roo.data.Record constructor.
12175  * 
12176  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12177  * in the reply previously. 
12178  * 
12179  * <p>
12180  * Example code:
12181  * <pre><code>
12182 var RecordDef = Roo.data.Record.create([
12183     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12184     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12185 ]);
12186 var myReader = new Roo.data.JsonReader({
12187     totalProperty: "results",    // The property which contains the total dataset size (optional)
12188     root: "rows",                // The property which contains an Array of row objects
12189     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12190 }, RecordDef);
12191 </code></pre>
12192  * <p>
12193  * This would consume a JSON file like this:
12194  * <pre><code>
12195 { 'results': 2, 'rows': [
12196     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12197     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12198 }
12199 </code></pre>
12200  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12201  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12202  * paged from the remote server.
12203  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12204  * @cfg {String} root name of the property which contains the Array of row objects.
12205  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12206  * @cfg {Array} fields Array of field definition objects
12207  * @constructor
12208  * Create a new JsonReader
12209  * @param {Object} meta Metadata configuration options
12210  * @param {Object} recordType Either an Array of field definition objects,
12211  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12212  */
12213 Roo.data.JsonReader = function(meta, recordType){
12214     
12215     meta = meta || {};
12216     // set some defaults:
12217     Roo.applyIf(meta, {
12218         totalProperty: 'total',
12219         successProperty : 'success',
12220         root : 'data',
12221         id : 'id'
12222     });
12223     
12224     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12225 };
12226 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12227     
12228     /**
12229      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12230      * Used by Store query builder to append _requestMeta to params.
12231      * 
12232      */
12233     metaFromRemote : false,
12234     /**
12235      * This method is only used by a DataProxy which has retrieved data from a remote server.
12236      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12237      * @return {Object} data A data block which is used by an Roo.data.Store object as
12238      * a cache of Roo.data.Records.
12239      */
12240     read : function(response){
12241         var json = response.responseText;
12242        
12243         var o = /* eval:var:o */ eval("("+json+")");
12244         if(!o) {
12245             throw {message: "JsonReader.read: Json object not found"};
12246         }
12247         
12248         if(o.metaData){
12249             
12250             delete this.ef;
12251             this.metaFromRemote = true;
12252             this.meta = o.metaData;
12253             this.recordType = Roo.data.Record.create(o.metaData.fields);
12254             this.onMetaChange(this.meta, this.recordType, o);
12255         }
12256         return this.readRecords(o);
12257     },
12258
12259     // private function a store will implement
12260     onMetaChange : function(meta, recordType, o){
12261
12262     },
12263
12264     /**
12265          * @ignore
12266          */
12267     simpleAccess: function(obj, subsc) {
12268         return obj[subsc];
12269     },
12270
12271         /**
12272          * @ignore
12273          */
12274     getJsonAccessor: function(){
12275         var re = /[\[\.]/;
12276         return function(expr) {
12277             try {
12278                 return(re.test(expr))
12279                     ? new Function("obj", "return obj." + expr)
12280                     : function(obj){
12281                         return obj[expr];
12282                     };
12283             } catch(e){}
12284             return Roo.emptyFn;
12285         };
12286     }(),
12287
12288     /**
12289      * Create a data block containing Roo.data.Records from an XML document.
12290      * @param {Object} o An object which contains an Array of row objects in the property specified
12291      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12292      * which contains the total size of the dataset.
12293      * @return {Object} data A data block which is used by an Roo.data.Store object as
12294      * a cache of Roo.data.Records.
12295      */
12296     readRecords : function(o){
12297         /**
12298          * After any data loads, the raw JSON data is available for further custom processing.
12299          * @type Object
12300          */
12301         this.o = o;
12302         var s = this.meta, Record = this.recordType,
12303             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12304
12305 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12306         if (!this.ef) {
12307             if(s.totalProperty) {
12308                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12309                 }
12310                 if(s.successProperty) {
12311                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12312                 }
12313                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12314                 if (s.id) {
12315                         var g = this.getJsonAccessor(s.id);
12316                         this.getId = function(rec) {
12317                                 var r = g(rec);  
12318                                 return (r === undefined || r === "") ? null : r;
12319                         };
12320                 } else {
12321                         this.getId = function(){return null;};
12322                 }
12323             this.ef = [];
12324             for(var jj = 0; jj < fl; jj++){
12325                 f = fi[jj];
12326                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12327                 this.ef[jj] = this.getJsonAccessor(map);
12328             }
12329         }
12330
12331         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12332         if(s.totalProperty){
12333             var vt = parseInt(this.getTotal(o), 10);
12334             if(!isNaN(vt)){
12335                 totalRecords = vt;
12336             }
12337         }
12338         if(s.successProperty){
12339             var vs = this.getSuccess(o);
12340             if(vs === false || vs === 'false'){
12341                 success = false;
12342             }
12343         }
12344         var records = [];
12345         for(var i = 0; i < c; i++){
12346                 var n = root[i];
12347             var values = {};
12348             var id = this.getId(n);
12349             for(var j = 0; j < fl; j++){
12350                 f = fi[j];
12351             var v = this.ef[j](n);
12352             if (!f.convert) {
12353                 Roo.log('missing convert for ' + f.name);
12354                 Roo.log(f);
12355                 continue;
12356             }
12357             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12358             }
12359             var record = new Record(values, id);
12360             record.json = n;
12361             records[i] = record;
12362         }
12363         return {
12364             raw : o,
12365             success : success,
12366             records : records,
12367             totalRecords : totalRecords
12368         };
12369     }
12370 });/*
12371  * Based on:
12372  * Ext JS Library 1.1.1
12373  * Copyright(c) 2006-2007, Ext JS, LLC.
12374  *
12375  * Originally Released Under LGPL - original licence link has changed is not relivant.
12376  *
12377  * Fork - LGPL
12378  * <script type="text/javascript">
12379  */
12380
12381 /**
12382  * @class Roo.data.ArrayReader
12383  * @extends Roo.data.DataReader
12384  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12385  * Each element of that Array represents a row of data fields. The
12386  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12387  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12388  * <p>
12389  * Example code:.
12390  * <pre><code>
12391 var RecordDef = Roo.data.Record.create([
12392     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12393     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12394 ]);
12395 var myReader = new Roo.data.ArrayReader({
12396     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12397 }, RecordDef);
12398 </code></pre>
12399  * <p>
12400  * This would consume an Array like this:
12401  * <pre><code>
12402 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12403   </code></pre>
12404  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12405  * @constructor
12406  * Create a new JsonReader
12407  * @param {Object} meta Metadata configuration options.
12408  * @param {Object} recordType Either an Array of field definition objects
12409  * as specified to {@link Roo.data.Record#create},
12410  * or an {@link Roo.data.Record} object
12411  * created using {@link Roo.data.Record#create}.
12412  */
12413 Roo.data.ArrayReader = function(meta, recordType){
12414     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12415 };
12416
12417 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12418     /**
12419      * Create a data block containing Roo.data.Records from an XML document.
12420      * @param {Object} o An Array of row objects which represents the dataset.
12421      * @return {Object} data A data block which is used by an Roo.data.Store object as
12422      * a cache of Roo.data.Records.
12423      */
12424     readRecords : function(o){
12425         var sid = this.meta ? this.meta.id : null;
12426         var recordType = this.recordType, fields = recordType.prototype.fields;
12427         var records = [];
12428         var root = o;
12429             for(var i = 0; i < root.length; i++){
12430                     var n = root[i];
12431                 var values = {};
12432                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12433                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12434                 var f = fields.items[j];
12435                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12436                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12437                 v = f.convert(v);
12438                 values[f.name] = v;
12439             }
12440                 var record = new recordType(values, id);
12441                 record.json = n;
12442                 records[records.length] = record;
12443             }
12444             return {
12445                 records : records,
12446                 totalRecords : records.length
12447             };
12448     }
12449 });/*
12450  * - LGPL
12451  * * 
12452  */
12453
12454 /**
12455  * @class Roo.bootstrap.ComboBox
12456  * @extends Roo.bootstrap.TriggerField
12457  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12458  * @cfg {Boolean} append (true|false) default false
12459  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12460  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12461  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12462  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12463  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12464  * @cfg {Boolean} animate default true
12465  * @cfg {Boolean} emptyResultText only for touch device
12466  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12467  * @cfg {String} emptyTitle default ''
12468  * @constructor
12469  * Create a new ComboBox.
12470  * @param {Object} config Configuration options
12471  */
12472 Roo.bootstrap.ComboBox = function(config){
12473     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12474     this.addEvents({
12475         /**
12476          * @event expand
12477          * Fires when the dropdown list is expanded
12478         * @param {Roo.bootstrap.ComboBox} combo This combo box
12479         */
12480         'expand' : true,
12481         /**
12482          * @event collapse
12483          * Fires when the dropdown list is collapsed
12484         * @param {Roo.bootstrap.ComboBox} combo This combo box
12485         */
12486         'collapse' : true,
12487         /**
12488          * @event beforeselect
12489          * Fires before a list item is selected. Return false to cancel the selection.
12490         * @param {Roo.bootstrap.ComboBox} combo This combo box
12491         * @param {Roo.data.Record} record The data record returned from the underlying store
12492         * @param {Number} index The index of the selected item in the dropdown list
12493         */
12494         'beforeselect' : true,
12495         /**
12496          * @event select
12497          * Fires when a list item is selected
12498         * @param {Roo.bootstrap.ComboBox} combo This combo box
12499         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12500         * @param {Number} index The index of the selected item in the dropdown list
12501         */
12502         'select' : true,
12503         /**
12504          * @event beforequery
12505          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12506          * The event object passed has these properties:
12507         * @param {Roo.bootstrap.ComboBox} combo This combo box
12508         * @param {String} query The query
12509         * @param {Boolean} forceAll true to force "all" query
12510         * @param {Boolean} cancel true to cancel the query
12511         * @param {Object} e The query event object
12512         */
12513         'beforequery': true,
12514          /**
12515          * @event add
12516          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12517         * @param {Roo.bootstrap.ComboBox} combo This combo box
12518         */
12519         'add' : true,
12520         /**
12521          * @event edit
12522          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12523         * @param {Roo.bootstrap.ComboBox} combo This combo box
12524         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12525         */
12526         'edit' : true,
12527         /**
12528          * @event remove
12529          * Fires when the remove value from the combobox array
12530         * @param {Roo.bootstrap.ComboBox} combo This combo box
12531         */
12532         'remove' : true,
12533         /**
12534          * @event afterremove
12535          * Fires when the remove value from the combobox array
12536         * @param {Roo.bootstrap.ComboBox} combo This combo box
12537         */
12538         'afterremove' : true,
12539         /**
12540          * @event specialfilter
12541          * Fires when specialfilter
12542             * @param {Roo.bootstrap.ComboBox} combo This combo box
12543             */
12544         'specialfilter' : true,
12545         /**
12546          * @event tick
12547          * Fires when tick the element
12548             * @param {Roo.bootstrap.ComboBox} combo This combo box
12549             */
12550         'tick' : true,
12551         /**
12552          * @event touchviewdisplay
12553          * Fires when touch view require special display (default is using displayField)
12554             * @param {Roo.bootstrap.ComboBox} combo This combo box
12555             * @param {Object} cfg set html .
12556             */
12557         'touchviewdisplay' : true
12558         
12559     });
12560     
12561     this.item = [];
12562     this.tickItems = [];
12563     
12564     this.selectedIndex = -1;
12565     if(this.mode == 'local'){
12566         if(config.queryDelay === undefined){
12567             this.queryDelay = 10;
12568         }
12569         if(config.minChars === undefined){
12570             this.minChars = 0;
12571         }
12572     }
12573 };
12574
12575 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12576      
12577     /**
12578      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12579      * rendering into an Roo.Editor, defaults to false)
12580      */
12581     /**
12582      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12583      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12584      */
12585     /**
12586      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12587      */
12588     /**
12589      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12590      * the dropdown list (defaults to undefined, with no header element)
12591      */
12592
12593      /**
12594      * @cfg {String/Roo.Template} tpl The template to use to render the output
12595      */
12596      
12597      /**
12598      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12599      */
12600     listWidth: undefined,
12601     /**
12602      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12603      * mode = 'remote' or 'text' if mode = 'local')
12604      */
12605     displayField: undefined,
12606     
12607     /**
12608      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12609      * mode = 'remote' or 'value' if mode = 'local'). 
12610      * Note: use of a valueField requires the user make a selection
12611      * in order for a value to be mapped.
12612      */
12613     valueField: undefined,
12614     /**
12615      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12616      */
12617     modalTitle : '',
12618     
12619     /**
12620      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12621      * field's data value (defaults to the underlying DOM element's name)
12622      */
12623     hiddenName: undefined,
12624     /**
12625      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12626      */
12627     listClass: '',
12628     /**
12629      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12630      */
12631     selectedClass: 'active',
12632     
12633     /**
12634      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12635      */
12636     shadow:'sides',
12637     /**
12638      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12639      * anchor positions (defaults to 'tl-bl')
12640      */
12641     listAlign: 'tl-bl?',
12642     /**
12643      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12644      */
12645     maxHeight: 300,
12646     /**
12647      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12648      * query specified by the allQuery config option (defaults to 'query')
12649      */
12650     triggerAction: 'query',
12651     /**
12652      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12653      * (defaults to 4, does not apply if editable = false)
12654      */
12655     minChars : 4,
12656     /**
12657      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12658      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12659      */
12660     typeAhead: false,
12661     /**
12662      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12663      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12664      */
12665     queryDelay: 500,
12666     /**
12667      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12668      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12669      */
12670     pageSize: 0,
12671     /**
12672      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12673      * when editable = true (defaults to false)
12674      */
12675     selectOnFocus:false,
12676     /**
12677      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12678      */
12679     queryParam: 'query',
12680     /**
12681      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12682      * when mode = 'remote' (defaults to 'Loading...')
12683      */
12684     loadingText: 'Loading...',
12685     /**
12686      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12687      */
12688     resizable: false,
12689     /**
12690      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12691      */
12692     handleHeight : 8,
12693     /**
12694      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12695      * traditional select (defaults to true)
12696      */
12697     editable: true,
12698     /**
12699      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12700      */
12701     allQuery: '',
12702     /**
12703      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12704      */
12705     mode: 'remote',
12706     /**
12707      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12708      * listWidth has a higher value)
12709      */
12710     minListWidth : 70,
12711     /**
12712      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12713      * allow the user to set arbitrary text into the field (defaults to false)
12714      */
12715     forceSelection:false,
12716     /**
12717      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12718      * if typeAhead = true (defaults to 250)
12719      */
12720     typeAheadDelay : 250,
12721     /**
12722      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12723      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12724      */
12725     valueNotFoundText : undefined,
12726     /**
12727      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12728      */
12729     blockFocus : false,
12730     
12731     /**
12732      * @cfg {Boolean} disableClear Disable showing of clear button.
12733      */
12734     disableClear : false,
12735     /**
12736      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12737      */
12738     alwaysQuery : false,
12739     
12740     /**
12741      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12742      */
12743     multiple : false,
12744     
12745     /**
12746      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12747      */
12748     invalidClass : "has-warning",
12749     
12750     /**
12751      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12752      */
12753     validClass : "has-success",
12754     
12755     /**
12756      * @cfg {Boolean} specialFilter (true|false) special filter default false
12757      */
12758     specialFilter : false,
12759     
12760     /**
12761      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12762      */
12763     mobileTouchView : true,
12764     
12765     /**
12766      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12767      */
12768     useNativeIOS : false,
12769     
12770     ios_options : false,
12771     
12772     //private
12773     addicon : false,
12774     editicon: false,
12775     
12776     page: 0,
12777     hasQuery: false,
12778     append: false,
12779     loadNext: false,
12780     autoFocus : true,
12781     tickable : false,
12782     btnPosition : 'right',
12783     triggerList : true,
12784     showToggleBtn : true,
12785     animate : true,
12786     emptyResultText: 'Empty',
12787     triggerText : 'Select',
12788     emptyTitle : '',
12789     
12790     // element that contains real text value.. (when hidden is used..)
12791     
12792     getAutoCreate : function()
12793     {   
12794         var cfg = false;
12795         //render
12796         /*
12797          * Render classic select for iso
12798          */
12799         
12800         if(Roo.isIOS && this.useNativeIOS){
12801             cfg = this.getAutoCreateNativeIOS();
12802             return cfg;
12803         }
12804         
12805         /*
12806          * Touch Devices
12807          */
12808         
12809         if(Roo.isTouch && this.mobileTouchView){
12810             cfg = this.getAutoCreateTouchView();
12811             return cfg;;
12812         }
12813         
12814         /*
12815          *  Normal ComboBox
12816          */
12817         if(!this.tickable){
12818             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12819             return cfg;
12820         }
12821         
12822         /*
12823          *  ComboBox with tickable selections
12824          */
12825              
12826         var align = this.labelAlign || this.parentLabelAlign();
12827         
12828         cfg = {
12829             cls : 'form-group roo-combobox-tickable' //input-group
12830         };
12831         
12832         var btn_text_select = '';
12833         var btn_text_done = '';
12834         var btn_text_cancel = '';
12835         
12836         if (this.btn_text_show) {
12837             btn_text_select = 'Select';
12838             btn_text_done = 'Done';
12839             btn_text_cancel = 'Cancel'; 
12840         }
12841         
12842         var buttons = {
12843             tag : 'div',
12844             cls : 'tickable-buttons',
12845             cn : [
12846                 {
12847                     tag : 'button',
12848                     type : 'button',
12849                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12850                     //html : this.triggerText
12851                     html: btn_text_select
12852                 },
12853                 {
12854                     tag : 'button',
12855                     type : 'button',
12856                     name : 'ok',
12857                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12858                     //html : 'Done'
12859                     html: btn_text_done
12860                 },
12861                 {
12862                     tag : 'button',
12863                     type : 'button',
12864                     name : 'cancel',
12865                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12866                     //html : 'Cancel'
12867                     html: btn_text_cancel
12868                 }
12869             ]
12870         };
12871         
12872         if(this.editable){
12873             buttons.cn.unshift({
12874                 tag: 'input',
12875                 cls: 'roo-select2-search-field-input'
12876             });
12877         }
12878         
12879         var _this = this;
12880         
12881         Roo.each(buttons.cn, function(c){
12882             if (_this.size) {
12883                 c.cls += ' btn-' + _this.size;
12884             }
12885
12886             if (_this.disabled) {
12887                 c.disabled = true;
12888             }
12889         });
12890         
12891         var box = {
12892             tag: 'div',
12893             cn: [
12894                 {
12895                     tag: 'input',
12896                     type : 'hidden',
12897                     cls: 'form-hidden-field'
12898                 },
12899                 {
12900                     tag: 'ul',
12901                     cls: 'roo-select2-choices',
12902                     cn:[
12903                         {
12904                             tag: 'li',
12905                             cls: 'roo-select2-search-field',
12906                             cn: [
12907                                 buttons
12908                             ]
12909                         }
12910                     ]
12911                 }
12912             ]
12913         };
12914         
12915         var combobox = {
12916             cls: 'roo-select2-container input-group roo-select2-container-multi',
12917             cn: [
12918                 box
12919 //                {
12920 //                    tag: 'ul',
12921 //                    cls: 'typeahead typeahead-long dropdown-menu',
12922 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12923 //                }
12924             ]
12925         };
12926         
12927         if(this.hasFeedback && !this.allowBlank){
12928             
12929             var feedback = {
12930                 tag: 'span',
12931                 cls: 'glyphicon form-control-feedback'
12932             };
12933
12934             combobox.cn.push(feedback);
12935         }
12936         
12937         
12938         if (align ==='left' && this.fieldLabel.length) {
12939             
12940             cfg.cls += ' roo-form-group-label-left';
12941             
12942             cfg.cn = [
12943                 {
12944                     tag : 'i',
12945                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12946                     tooltip : 'This field is required'
12947                 },
12948                 {
12949                     tag: 'label',
12950                     'for' :  id,
12951                     cls : 'control-label',
12952                     html : this.fieldLabel
12953
12954                 },
12955                 {
12956                     cls : "", 
12957                     cn: [
12958                         combobox
12959                     ]
12960                 }
12961
12962             ];
12963             
12964             var labelCfg = cfg.cn[1];
12965             var contentCfg = cfg.cn[2];
12966             
12967
12968             if(this.indicatorpos == 'right'){
12969                 
12970                 cfg.cn = [
12971                     {
12972                         tag: 'label',
12973                         'for' :  id,
12974                         cls : 'control-label',
12975                         cn : [
12976                             {
12977                                 tag : 'span',
12978                                 html : this.fieldLabel
12979                             },
12980                             {
12981                                 tag : 'i',
12982                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12983                                 tooltip : 'This field is required'
12984                             }
12985                         ]
12986                     },
12987                     {
12988                         cls : "",
12989                         cn: [
12990                             combobox
12991                         ]
12992                     }
12993
12994                 ];
12995                 
12996                 
12997                 
12998                 labelCfg = cfg.cn[0];
12999                 contentCfg = cfg.cn[1];
13000             
13001             }
13002             
13003             if(this.labelWidth > 12){
13004                 labelCfg.style = "width: " + this.labelWidth + 'px';
13005             }
13006             
13007             if(this.labelWidth < 13 && this.labelmd == 0){
13008                 this.labelmd = this.labelWidth;
13009             }
13010             
13011             if(this.labellg > 0){
13012                 labelCfg.cls += ' col-lg-' + this.labellg;
13013                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13014             }
13015             
13016             if(this.labelmd > 0){
13017                 labelCfg.cls += ' col-md-' + this.labelmd;
13018                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13019             }
13020             
13021             if(this.labelsm > 0){
13022                 labelCfg.cls += ' col-sm-' + this.labelsm;
13023                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13024             }
13025             
13026             if(this.labelxs > 0){
13027                 labelCfg.cls += ' col-xs-' + this.labelxs;
13028                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13029             }
13030                 
13031                 
13032         } else if ( this.fieldLabel.length) {
13033 //                Roo.log(" label");
13034                  cfg.cn = [
13035                     {
13036                         tag : 'i',
13037                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13038                         tooltip : 'This field is required'
13039                     },
13040                     {
13041                         tag: 'label',
13042                         //cls : 'input-group-addon',
13043                         html : this.fieldLabel
13044                     },
13045                     combobox
13046                 ];
13047                 
13048                 if(this.indicatorpos == 'right'){
13049                     cfg.cn = [
13050                         {
13051                             tag: 'label',
13052                             //cls : 'input-group-addon',
13053                             html : this.fieldLabel
13054                         },
13055                         {
13056                             tag : 'i',
13057                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13058                             tooltip : 'This field is required'
13059                         },
13060                         combobox
13061                     ];
13062                     
13063                 }
13064
13065         } else {
13066             
13067 //                Roo.log(" no label && no align");
13068                 cfg = combobox
13069                      
13070                 
13071         }
13072          
13073         var settings=this;
13074         ['xs','sm','md','lg'].map(function(size){
13075             if (settings[size]) {
13076                 cfg.cls += ' col-' + size + '-' + settings[size];
13077             }
13078         });
13079         
13080         return cfg;
13081         
13082     },
13083     
13084     _initEventsCalled : false,
13085     
13086     // private
13087     initEvents: function()
13088     {   
13089         if (this._initEventsCalled) { // as we call render... prevent looping...
13090             return;
13091         }
13092         this._initEventsCalled = true;
13093         
13094         if (!this.store) {
13095             throw "can not find store for combo";
13096         }
13097         
13098         this.indicator = this.indicatorEl();
13099         
13100         this.store = Roo.factory(this.store, Roo.data);
13101         this.store.parent = this;
13102         
13103         // if we are building from html. then this element is so complex, that we can not really
13104         // use the rendered HTML.
13105         // so we have to trash and replace the previous code.
13106         if (Roo.XComponent.build_from_html) {
13107             // remove this element....
13108             var e = this.el.dom, k=0;
13109             while (e ) { e = e.previousSibling;  ++k;}
13110
13111             this.el.remove();
13112             
13113             this.el=false;
13114             this.rendered = false;
13115             
13116             this.render(this.parent().getChildContainer(true), k);
13117         }
13118         
13119         if(Roo.isIOS && this.useNativeIOS){
13120             this.initIOSView();
13121             return;
13122         }
13123         
13124         /*
13125          * Touch Devices
13126          */
13127         
13128         if(Roo.isTouch && this.mobileTouchView){
13129             this.initTouchView();
13130             return;
13131         }
13132         
13133         if(this.tickable){
13134             this.initTickableEvents();
13135             return;
13136         }
13137         
13138         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13139         
13140         if(this.hiddenName){
13141             
13142             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13143             
13144             this.hiddenField.dom.value =
13145                 this.hiddenValue !== undefined ? this.hiddenValue :
13146                 this.value !== undefined ? this.value : '';
13147
13148             // prevent input submission
13149             this.el.dom.removeAttribute('name');
13150             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13151              
13152              
13153         }
13154         //if(Roo.isGecko){
13155         //    this.el.dom.setAttribute('autocomplete', 'off');
13156         //}
13157         
13158         var cls = 'x-combo-list';
13159         
13160         //this.list = new Roo.Layer({
13161         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13162         //});
13163         
13164         var _this = this;
13165         
13166         (function(){
13167             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13168             _this.list.setWidth(lw);
13169         }).defer(100);
13170         
13171         this.list.on('mouseover', this.onViewOver, this);
13172         this.list.on('mousemove', this.onViewMove, this);
13173         this.list.on('scroll', this.onViewScroll, this);
13174         
13175         /*
13176         this.list.swallowEvent('mousewheel');
13177         this.assetHeight = 0;
13178
13179         if(this.title){
13180             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13181             this.assetHeight += this.header.getHeight();
13182         }
13183
13184         this.innerList = this.list.createChild({cls:cls+'-inner'});
13185         this.innerList.on('mouseover', this.onViewOver, this);
13186         this.innerList.on('mousemove', this.onViewMove, this);
13187         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13188         
13189         if(this.allowBlank && !this.pageSize && !this.disableClear){
13190             this.footer = this.list.createChild({cls:cls+'-ft'});
13191             this.pageTb = new Roo.Toolbar(this.footer);
13192            
13193         }
13194         if(this.pageSize){
13195             this.footer = this.list.createChild({cls:cls+'-ft'});
13196             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13197                     {pageSize: this.pageSize});
13198             
13199         }
13200         
13201         if (this.pageTb && this.allowBlank && !this.disableClear) {
13202             var _this = this;
13203             this.pageTb.add(new Roo.Toolbar.Fill(), {
13204                 cls: 'x-btn-icon x-btn-clear',
13205                 text: '&#160;',
13206                 handler: function()
13207                 {
13208                     _this.collapse();
13209                     _this.clearValue();
13210                     _this.onSelect(false, -1);
13211                 }
13212             });
13213         }
13214         if (this.footer) {
13215             this.assetHeight += this.footer.getHeight();
13216         }
13217         */
13218             
13219         if(!this.tpl){
13220             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13221         }
13222
13223         this.view = new Roo.View(this.list, this.tpl, {
13224             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13225         });
13226         //this.view.wrapEl.setDisplayed(false);
13227         this.view.on('click', this.onViewClick, this);
13228         
13229         
13230         this.store.on('beforeload', this.onBeforeLoad, this);
13231         this.store.on('load', this.onLoad, this);
13232         this.store.on('loadexception', this.onLoadException, this);
13233         /*
13234         if(this.resizable){
13235             this.resizer = new Roo.Resizable(this.list,  {
13236                pinned:true, handles:'se'
13237             });
13238             this.resizer.on('resize', function(r, w, h){
13239                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13240                 this.listWidth = w;
13241                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13242                 this.restrictHeight();
13243             }, this);
13244             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13245         }
13246         */
13247         if(!this.editable){
13248             this.editable = true;
13249             this.setEditable(false);
13250         }
13251         
13252         /*
13253         
13254         if (typeof(this.events.add.listeners) != 'undefined') {
13255             
13256             this.addicon = this.wrap.createChild(
13257                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13258        
13259             this.addicon.on('click', function(e) {
13260                 this.fireEvent('add', this);
13261             }, this);
13262         }
13263         if (typeof(this.events.edit.listeners) != 'undefined') {
13264             
13265             this.editicon = this.wrap.createChild(
13266                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13267             if (this.addicon) {
13268                 this.editicon.setStyle('margin-left', '40px');
13269             }
13270             this.editicon.on('click', function(e) {
13271                 
13272                 // we fire even  if inothing is selected..
13273                 this.fireEvent('edit', this, this.lastData );
13274                 
13275             }, this);
13276         }
13277         */
13278         
13279         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13280             "up" : function(e){
13281                 this.inKeyMode = true;
13282                 this.selectPrev();
13283             },
13284
13285             "down" : function(e){
13286                 if(!this.isExpanded()){
13287                     this.onTriggerClick();
13288                 }else{
13289                     this.inKeyMode = true;
13290                     this.selectNext();
13291                 }
13292             },
13293
13294             "enter" : function(e){
13295 //                this.onViewClick();
13296                 //return true;
13297                 this.collapse();
13298                 
13299                 if(this.fireEvent("specialkey", this, e)){
13300                     this.onViewClick(false);
13301                 }
13302                 
13303                 return true;
13304             },
13305
13306             "esc" : function(e){
13307                 this.collapse();
13308             },
13309
13310             "tab" : function(e){
13311                 this.collapse();
13312                 
13313                 if(this.fireEvent("specialkey", this, e)){
13314                     this.onViewClick(false);
13315                 }
13316                 
13317                 return true;
13318             },
13319
13320             scope : this,
13321
13322             doRelay : function(foo, bar, hname){
13323                 if(hname == 'down' || this.scope.isExpanded()){
13324                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13325                 }
13326                 return true;
13327             },
13328
13329             forceKeyDown: true
13330         });
13331         
13332         
13333         this.queryDelay = Math.max(this.queryDelay || 10,
13334                 this.mode == 'local' ? 10 : 250);
13335         
13336         
13337         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13338         
13339         if(this.typeAhead){
13340             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13341         }
13342         if(this.editable !== false){
13343             this.inputEl().on("keyup", this.onKeyUp, this);
13344         }
13345         if(this.forceSelection){
13346             this.inputEl().on('blur', this.doForce, this);
13347         }
13348         
13349         if(this.multiple){
13350             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13351             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13352         }
13353     },
13354     
13355     initTickableEvents: function()
13356     {   
13357         this.createList();
13358         
13359         if(this.hiddenName){
13360             
13361             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13362             
13363             this.hiddenField.dom.value =
13364                 this.hiddenValue !== undefined ? this.hiddenValue :
13365                 this.value !== undefined ? this.value : '';
13366
13367             // prevent input submission
13368             this.el.dom.removeAttribute('name');
13369             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13370              
13371              
13372         }
13373         
13374 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13375         
13376         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13377         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13378         if(this.triggerList){
13379             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13380         }
13381          
13382         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13383         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13384         
13385         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13386         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13387         
13388         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13389         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13390         
13391         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13392         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13393         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13394         
13395         this.okBtn.hide();
13396         this.cancelBtn.hide();
13397         
13398         var _this = this;
13399         
13400         (function(){
13401             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13402             _this.list.setWidth(lw);
13403         }).defer(100);
13404         
13405         this.list.on('mouseover', this.onViewOver, this);
13406         this.list.on('mousemove', this.onViewMove, this);
13407         
13408         this.list.on('scroll', this.onViewScroll, this);
13409         
13410         if(!this.tpl){
13411             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13412         }
13413
13414         this.view = new Roo.View(this.list, this.tpl, {
13415             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13416         });
13417         
13418         //this.view.wrapEl.setDisplayed(false);
13419         this.view.on('click', this.onViewClick, this);
13420         
13421         
13422         
13423         this.store.on('beforeload', this.onBeforeLoad, this);
13424         this.store.on('load', this.onLoad, this);
13425         this.store.on('loadexception', this.onLoadException, this);
13426         
13427         if(this.editable){
13428             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13429                 "up" : function(e){
13430                     this.inKeyMode = true;
13431                     this.selectPrev();
13432                 },
13433
13434                 "down" : function(e){
13435                     this.inKeyMode = true;
13436                     this.selectNext();
13437                 },
13438
13439                 "enter" : function(e){
13440                     if(this.fireEvent("specialkey", this, e)){
13441                         this.onViewClick(false);
13442                     }
13443                     
13444                     return true;
13445                 },
13446
13447                 "esc" : function(e){
13448                     this.onTickableFooterButtonClick(e, false, false);
13449                 },
13450
13451                 "tab" : function(e){
13452                     this.fireEvent("specialkey", this, e);
13453                     
13454                     this.onTickableFooterButtonClick(e, false, false);
13455                     
13456                     return true;
13457                 },
13458
13459                 scope : this,
13460
13461                 doRelay : function(e, fn, key){
13462                     if(this.scope.isExpanded()){
13463                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13464                     }
13465                     return true;
13466                 },
13467
13468                 forceKeyDown: true
13469             });
13470         }
13471         
13472         this.queryDelay = Math.max(this.queryDelay || 10,
13473                 this.mode == 'local' ? 10 : 250);
13474         
13475         
13476         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13477         
13478         if(this.typeAhead){
13479             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13480         }
13481         
13482         if(this.editable !== false){
13483             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13484         }
13485         
13486         this.indicator = this.indicatorEl();
13487         
13488         if(this.indicator){
13489             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13490             this.indicator.hide();
13491         }
13492         
13493     },
13494
13495     onDestroy : function(){
13496         if(this.view){
13497             this.view.setStore(null);
13498             this.view.el.removeAllListeners();
13499             this.view.el.remove();
13500             this.view.purgeListeners();
13501         }
13502         if(this.list){
13503             this.list.dom.innerHTML  = '';
13504         }
13505         
13506         if(this.store){
13507             this.store.un('beforeload', this.onBeforeLoad, this);
13508             this.store.un('load', this.onLoad, this);
13509             this.store.un('loadexception', this.onLoadException, this);
13510         }
13511         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13512     },
13513
13514     // private
13515     fireKey : function(e){
13516         if(e.isNavKeyPress() && !this.list.isVisible()){
13517             this.fireEvent("specialkey", this, e);
13518         }
13519     },
13520
13521     // private
13522     onResize: function(w, h){
13523 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13524 //        
13525 //        if(typeof w != 'number'){
13526 //            // we do not handle it!?!?
13527 //            return;
13528 //        }
13529 //        var tw = this.trigger.getWidth();
13530 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13531 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13532 //        var x = w - tw;
13533 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13534 //            
13535 //        //this.trigger.setStyle('left', x+'px');
13536 //        
13537 //        if(this.list && this.listWidth === undefined){
13538 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13539 //            this.list.setWidth(lw);
13540 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13541 //        }
13542         
13543     
13544         
13545     },
13546
13547     /**
13548      * Allow or prevent the user from directly editing the field text.  If false is passed,
13549      * the user will only be able to select from the items defined in the dropdown list.  This method
13550      * is the runtime equivalent of setting the 'editable' config option at config time.
13551      * @param {Boolean} value True to allow the user to directly edit the field text
13552      */
13553     setEditable : function(value){
13554         if(value == this.editable){
13555             return;
13556         }
13557         this.editable = value;
13558         if(!value){
13559             this.inputEl().dom.setAttribute('readOnly', true);
13560             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13561             this.inputEl().addClass('x-combo-noedit');
13562         }else{
13563             this.inputEl().dom.setAttribute('readOnly', false);
13564             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13565             this.inputEl().removeClass('x-combo-noedit');
13566         }
13567     },
13568
13569     // private
13570     
13571     onBeforeLoad : function(combo,opts){
13572         if(!this.hasFocus){
13573             return;
13574         }
13575          if (!opts.add) {
13576             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13577          }
13578         this.restrictHeight();
13579         this.selectedIndex = -1;
13580     },
13581
13582     // private
13583     onLoad : function(){
13584         
13585         this.hasQuery = false;
13586         
13587         if(!this.hasFocus){
13588             return;
13589         }
13590         
13591         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13592             this.loading.hide();
13593         }
13594         
13595         if(this.store.getCount() > 0){
13596             
13597             this.expand();
13598             this.restrictHeight();
13599             if(this.lastQuery == this.allQuery){
13600                 if(this.editable && !this.tickable){
13601                     this.inputEl().dom.select();
13602                 }
13603                 
13604                 if(
13605                     !this.selectByValue(this.value, true) &&
13606                     this.autoFocus && 
13607                     (
13608                         !this.store.lastOptions ||
13609                         typeof(this.store.lastOptions.add) == 'undefined' || 
13610                         this.store.lastOptions.add != true
13611                     )
13612                 ){
13613                     this.select(0, true);
13614                 }
13615             }else{
13616                 if(this.autoFocus){
13617                     this.selectNext();
13618                 }
13619                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13620                     this.taTask.delay(this.typeAheadDelay);
13621                 }
13622             }
13623         }else{
13624             this.onEmptyResults();
13625         }
13626         
13627         //this.el.focus();
13628     },
13629     // private
13630     onLoadException : function()
13631     {
13632         this.hasQuery = false;
13633         
13634         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13635             this.loading.hide();
13636         }
13637         
13638         if(this.tickable && this.editable){
13639             return;
13640         }
13641         
13642         this.collapse();
13643         // only causes errors at present
13644         //Roo.log(this.store.reader.jsonData);
13645         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13646             // fixme
13647             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13648         //}
13649         
13650         
13651     },
13652     // private
13653     onTypeAhead : function(){
13654         if(this.store.getCount() > 0){
13655             var r = this.store.getAt(0);
13656             var newValue = r.data[this.displayField];
13657             var len = newValue.length;
13658             var selStart = this.getRawValue().length;
13659             
13660             if(selStart != len){
13661                 this.setRawValue(newValue);
13662                 this.selectText(selStart, newValue.length);
13663             }
13664         }
13665     },
13666
13667     // private
13668     onSelect : function(record, index){
13669         
13670         if(this.fireEvent('beforeselect', this, record, index) !== false){
13671         
13672             this.setFromData(index > -1 ? record.data : false);
13673             
13674             this.collapse();
13675             this.fireEvent('select', this, record, index);
13676         }
13677     },
13678
13679     /**
13680      * Returns the currently selected field value or empty string if no value is set.
13681      * @return {String} value The selected value
13682      */
13683     getValue : function()
13684     {
13685         if(Roo.isIOS && this.useNativeIOS){
13686             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13687         }
13688         
13689         if(this.multiple){
13690             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13691         }
13692         
13693         if(this.valueField){
13694             return typeof this.value != 'undefined' ? this.value : '';
13695         }else{
13696             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13697         }
13698     },
13699     
13700     getRawValue : function()
13701     {
13702         if(Roo.isIOS && this.useNativeIOS){
13703             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13704         }
13705         
13706         var v = this.inputEl().getValue();
13707         
13708         return v;
13709     },
13710
13711     /**
13712      * Clears any text/value currently set in the field
13713      */
13714     clearValue : function(){
13715         
13716         if(this.hiddenField){
13717             this.hiddenField.dom.value = '';
13718         }
13719         this.value = '';
13720         this.setRawValue('');
13721         this.lastSelectionText = '';
13722         this.lastData = false;
13723         
13724         var close = this.closeTriggerEl();
13725         
13726         if(close){
13727             close.hide();
13728         }
13729         
13730         this.validate();
13731         
13732     },
13733
13734     /**
13735      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13736      * will be displayed in the field.  If the value does not match the data value of an existing item,
13737      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13738      * Otherwise the field will be blank (although the value will still be set).
13739      * @param {String} value The value to match
13740      */
13741     setValue : function(v)
13742     {
13743         if(Roo.isIOS && this.useNativeIOS){
13744             this.setIOSValue(v);
13745             return;
13746         }
13747         
13748         if(this.multiple){
13749             this.syncValue();
13750             return;
13751         }
13752         
13753         var text = v;
13754         if(this.valueField){
13755             var r = this.findRecord(this.valueField, v);
13756             if(r){
13757                 text = r.data[this.displayField];
13758             }else if(this.valueNotFoundText !== undefined){
13759                 text = this.valueNotFoundText;
13760             }
13761         }
13762         this.lastSelectionText = text;
13763         if(this.hiddenField){
13764             this.hiddenField.dom.value = v;
13765         }
13766         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13767         this.value = v;
13768         
13769         var close = this.closeTriggerEl();
13770         
13771         if(close){
13772             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13773         }
13774         
13775         this.validate();
13776     },
13777     /**
13778      * @property {Object} the last set data for the element
13779      */
13780     
13781     lastData : false,
13782     /**
13783      * Sets the value of the field based on a object which is related to the record format for the store.
13784      * @param {Object} value the value to set as. or false on reset?
13785      */
13786     setFromData : function(o){
13787         
13788         if(this.multiple){
13789             this.addItem(o);
13790             return;
13791         }
13792             
13793         var dv = ''; // display value
13794         var vv = ''; // value value..
13795         this.lastData = o;
13796         if (this.displayField) {
13797             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13798         } else {
13799             // this is an error condition!!!
13800             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13801         }
13802         
13803         if(this.valueField){
13804             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13805         }
13806         
13807         var close = this.closeTriggerEl();
13808         
13809         if(close){
13810             if(dv.length || vv * 1 > 0){
13811                 close.show() ;
13812                 this.blockFocus=true;
13813             } else {
13814                 close.hide();
13815             }             
13816         }
13817         
13818         if(this.hiddenField){
13819             this.hiddenField.dom.value = vv;
13820             
13821             this.lastSelectionText = dv;
13822             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13823             this.value = vv;
13824             return;
13825         }
13826         // no hidden field.. - we store the value in 'value', but still display
13827         // display field!!!!
13828         this.lastSelectionText = dv;
13829         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13830         this.value = vv;
13831         
13832         
13833         
13834     },
13835     // private
13836     reset : function(){
13837         // overridden so that last data is reset..
13838         
13839         if(this.multiple){
13840             this.clearItem();
13841             return;
13842         }
13843         
13844         this.setValue(this.originalValue);
13845         //this.clearInvalid();
13846         this.lastData = false;
13847         if (this.view) {
13848             this.view.clearSelections();
13849         }
13850         
13851         this.validate();
13852     },
13853     // private
13854     findRecord : function(prop, value){
13855         var record;
13856         if(this.store.getCount() > 0){
13857             this.store.each(function(r){
13858                 if(r.data[prop] == value){
13859                     record = r;
13860                     return false;
13861                 }
13862                 return true;
13863             });
13864         }
13865         return record;
13866     },
13867     
13868     getName: function()
13869     {
13870         // returns hidden if it's set..
13871         if (!this.rendered) {return ''};
13872         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13873         
13874     },
13875     // private
13876     onViewMove : function(e, t){
13877         this.inKeyMode = false;
13878     },
13879
13880     // private
13881     onViewOver : function(e, t){
13882         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13883             return;
13884         }
13885         var item = this.view.findItemFromChild(t);
13886         
13887         if(item){
13888             var index = this.view.indexOf(item);
13889             this.select(index, false);
13890         }
13891     },
13892
13893     // private
13894     onViewClick : function(view, doFocus, el, e)
13895     {
13896         var index = this.view.getSelectedIndexes()[0];
13897         
13898         var r = this.store.getAt(index);
13899         
13900         if(this.tickable){
13901             
13902             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13903                 return;
13904             }
13905             
13906             var rm = false;
13907             var _this = this;
13908             
13909             Roo.each(this.tickItems, function(v,k){
13910                 
13911                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13912                     Roo.log(v);
13913                     _this.tickItems.splice(k, 1);
13914                     
13915                     if(typeof(e) == 'undefined' && view == false){
13916                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13917                     }
13918                     
13919                     rm = true;
13920                     return;
13921                 }
13922             });
13923             
13924             if(rm){
13925                 return;
13926             }
13927             
13928             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13929                 this.tickItems.push(r.data);
13930             }
13931             
13932             if(typeof(e) == 'undefined' && view == false){
13933                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13934             }
13935                     
13936             return;
13937         }
13938         
13939         if(r){
13940             this.onSelect(r, index);
13941         }
13942         if(doFocus !== false && !this.blockFocus){
13943             this.inputEl().focus();
13944         }
13945     },
13946
13947     // private
13948     restrictHeight : function(){
13949         //this.innerList.dom.style.height = '';
13950         //var inner = this.innerList.dom;
13951         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13952         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13953         //this.list.beginUpdate();
13954         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13955         this.list.alignTo(this.inputEl(), this.listAlign);
13956         this.list.alignTo(this.inputEl(), this.listAlign);
13957         //this.list.endUpdate();
13958     },
13959
13960     // private
13961     onEmptyResults : function(){
13962         
13963         if(this.tickable && this.editable){
13964             this.restrictHeight();
13965             return;
13966         }
13967         
13968         this.collapse();
13969     },
13970
13971     /**
13972      * Returns true if the dropdown list is expanded, else false.
13973      */
13974     isExpanded : function(){
13975         return this.list.isVisible();
13976     },
13977
13978     /**
13979      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13980      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13981      * @param {String} value The data value of the item to select
13982      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13983      * selected item if it is not currently in view (defaults to true)
13984      * @return {Boolean} True if the value matched an item in the list, else false
13985      */
13986     selectByValue : function(v, scrollIntoView){
13987         if(v !== undefined && v !== null){
13988             var r = this.findRecord(this.valueField || this.displayField, v);
13989             if(r){
13990                 this.select(this.store.indexOf(r), scrollIntoView);
13991                 return true;
13992             }
13993         }
13994         return false;
13995     },
13996
13997     /**
13998      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13999      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14000      * @param {Number} index The zero-based index of the list item to select
14001      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14002      * selected item if it is not currently in view (defaults to true)
14003      */
14004     select : function(index, scrollIntoView){
14005         this.selectedIndex = index;
14006         this.view.select(index);
14007         if(scrollIntoView !== false){
14008             var el = this.view.getNode(index);
14009             /*
14010              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14011              */
14012             if(el){
14013                 this.list.scrollChildIntoView(el, false);
14014             }
14015         }
14016     },
14017
14018     // private
14019     selectNext : function(){
14020         var ct = this.store.getCount();
14021         if(ct > 0){
14022             if(this.selectedIndex == -1){
14023                 this.select(0);
14024             }else if(this.selectedIndex < ct-1){
14025                 this.select(this.selectedIndex+1);
14026             }
14027         }
14028     },
14029
14030     // private
14031     selectPrev : function(){
14032         var ct = this.store.getCount();
14033         if(ct > 0){
14034             if(this.selectedIndex == -1){
14035                 this.select(0);
14036             }else if(this.selectedIndex != 0){
14037                 this.select(this.selectedIndex-1);
14038             }
14039         }
14040     },
14041
14042     // private
14043     onKeyUp : function(e){
14044         if(this.editable !== false && !e.isSpecialKey()){
14045             this.lastKey = e.getKey();
14046             this.dqTask.delay(this.queryDelay);
14047         }
14048     },
14049
14050     // private
14051     validateBlur : function(){
14052         return !this.list || !this.list.isVisible();   
14053     },
14054
14055     // private
14056     initQuery : function(){
14057         
14058         var v = this.getRawValue();
14059         
14060         if(this.tickable && this.editable){
14061             v = this.tickableInputEl().getValue();
14062         }
14063         
14064         this.doQuery(v);
14065     },
14066
14067     // private
14068     doForce : function(){
14069         if(this.inputEl().dom.value.length > 0){
14070             this.inputEl().dom.value =
14071                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14072              
14073         }
14074     },
14075
14076     /**
14077      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14078      * query allowing the query action to be canceled if needed.
14079      * @param {String} query The SQL query to execute
14080      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14081      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14082      * saved in the current store (defaults to false)
14083      */
14084     doQuery : function(q, forceAll){
14085         
14086         if(q === undefined || q === null){
14087             q = '';
14088         }
14089         var qe = {
14090             query: q,
14091             forceAll: forceAll,
14092             combo: this,
14093             cancel:false
14094         };
14095         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14096             return false;
14097         }
14098         q = qe.query;
14099         
14100         forceAll = qe.forceAll;
14101         if(forceAll === true || (q.length >= this.minChars)){
14102             
14103             this.hasQuery = true;
14104             
14105             if(this.lastQuery != q || this.alwaysQuery){
14106                 this.lastQuery = q;
14107                 if(this.mode == 'local'){
14108                     this.selectedIndex = -1;
14109                     if(forceAll){
14110                         this.store.clearFilter();
14111                     }else{
14112                         
14113                         if(this.specialFilter){
14114                             this.fireEvent('specialfilter', this);
14115                             this.onLoad();
14116                             return;
14117                         }
14118                         
14119                         this.store.filter(this.displayField, q);
14120                     }
14121                     
14122                     this.store.fireEvent("datachanged", this.store);
14123                     
14124                     this.onLoad();
14125                     
14126                     
14127                 }else{
14128                     
14129                     this.store.baseParams[this.queryParam] = q;
14130                     
14131                     var options = {params : this.getParams(q)};
14132                     
14133                     if(this.loadNext){
14134                         options.add = true;
14135                         options.params.start = this.page * this.pageSize;
14136                     }
14137                     
14138                     this.store.load(options);
14139                     
14140                     /*
14141                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14142                      *  we should expand the list on onLoad
14143                      *  so command out it
14144                      */
14145 //                    this.expand();
14146                 }
14147             }else{
14148                 this.selectedIndex = -1;
14149                 this.onLoad();   
14150             }
14151         }
14152         
14153         this.loadNext = false;
14154     },
14155     
14156     // private
14157     getParams : function(q){
14158         var p = {};
14159         //p[this.queryParam] = q;
14160         
14161         if(this.pageSize){
14162             p.start = 0;
14163             p.limit = this.pageSize;
14164         }
14165         return p;
14166     },
14167
14168     /**
14169      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14170      */
14171     collapse : function(){
14172         if(!this.isExpanded()){
14173             return;
14174         }
14175         
14176         this.list.hide();
14177         
14178         this.hasFocus = false;
14179         
14180         if(this.tickable){
14181             this.okBtn.hide();
14182             this.cancelBtn.hide();
14183             this.trigger.show();
14184             
14185             if(this.editable){
14186                 this.tickableInputEl().dom.value = '';
14187                 this.tickableInputEl().blur();
14188             }
14189             
14190         }
14191         
14192         Roo.get(document).un('mousedown', this.collapseIf, this);
14193         Roo.get(document).un('mousewheel', this.collapseIf, this);
14194         if (!this.editable) {
14195             Roo.get(document).un('keydown', this.listKeyPress, this);
14196         }
14197         this.fireEvent('collapse', this);
14198         
14199         this.validate();
14200     },
14201
14202     // private
14203     collapseIf : function(e){
14204         var in_combo  = e.within(this.el);
14205         var in_list =  e.within(this.list);
14206         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14207         
14208         if (in_combo || in_list || is_list) {
14209             //e.stopPropagation();
14210             return;
14211         }
14212         
14213         if(this.tickable){
14214             this.onTickableFooterButtonClick(e, false, false);
14215         }
14216
14217         this.collapse();
14218         
14219     },
14220
14221     /**
14222      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14223      */
14224     expand : function(){
14225        
14226         if(this.isExpanded() || !this.hasFocus){
14227             return;
14228         }
14229         
14230         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14231         this.list.setWidth(lw);
14232         
14233         Roo.log('expand');
14234         
14235         this.list.show();
14236         
14237         this.restrictHeight();
14238         
14239         if(this.tickable){
14240             
14241             this.tickItems = Roo.apply([], this.item);
14242             
14243             this.okBtn.show();
14244             this.cancelBtn.show();
14245             this.trigger.hide();
14246             
14247             if(this.editable){
14248                 this.tickableInputEl().focus();
14249             }
14250             
14251         }
14252         
14253         Roo.get(document).on('mousedown', this.collapseIf, this);
14254         Roo.get(document).on('mousewheel', this.collapseIf, this);
14255         if (!this.editable) {
14256             Roo.get(document).on('keydown', this.listKeyPress, this);
14257         }
14258         
14259         this.fireEvent('expand', this);
14260     },
14261
14262     // private
14263     // Implements the default empty TriggerField.onTriggerClick function
14264     onTriggerClick : function(e)
14265     {
14266         Roo.log('trigger click');
14267         
14268         if(this.disabled || !this.triggerList){
14269             return;
14270         }
14271         
14272         this.page = 0;
14273         this.loadNext = false;
14274         
14275         if(this.isExpanded()){
14276             this.collapse();
14277             if (!this.blockFocus) {
14278                 this.inputEl().focus();
14279             }
14280             
14281         }else {
14282             this.hasFocus = true;
14283             if(this.triggerAction == 'all') {
14284                 this.doQuery(this.allQuery, true);
14285             } else {
14286                 this.doQuery(this.getRawValue());
14287             }
14288             if (!this.blockFocus) {
14289                 this.inputEl().focus();
14290             }
14291         }
14292     },
14293     
14294     onTickableTriggerClick : function(e)
14295     {
14296         if(this.disabled){
14297             return;
14298         }
14299         
14300         this.page = 0;
14301         this.loadNext = false;
14302         this.hasFocus = true;
14303         
14304         if(this.triggerAction == 'all') {
14305             this.doQuery(this.allQuery, true);
14306         } else {
14307             this.doQuery(this.getRawValue());
14308         }
14309     },
14310     
14311     onSearchFieldClick : function(e)
14312     {
14313         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14314             this.onTickableFooterButtonClick(e, false, false);
14315             return;
14316         }
14317         
14318         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14319             return;
14320         }
14321         
14322         this.page = 0;
14323         this.loadNext = false;
14324         this.hasFocus = true;
14325         
14326         if(this.triggerAction == 'all') {
14327             this.doQuery(this.allQuery, true);
14328         } else {
14329             this.doQuery(this.getRawValue());
14330         }
14331     },
14332     
14333     listKeyPress : function(e)
14334     {
14335         //Roo.log('listkeypress');
14336         // scroll to first matching element based on key pres..
14337         if (e.isSpecialKey()) {
14338             return false;
14339         }
14340         var k = String.fromCharCode(e.getKey()).toUpperCase();
14341         //Roo.log(k);
14342         var match  = false;
14343         var csel = this.view.getSelectedNodes();
14344         var cselitem = false;
14345         if (csel.length) {
14346             var ix = this.view.indexOf(csel[0]);
14347             cselitem  = this.store.getAt(ix);
14348             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14349                 cselitem = false;
14350             }
14351             
14352         }
14353         
14354         this.store.each(function(v) { 
14355             if (cselitem) {
14356                 // start at existing selection.
14357                 if (cselitem.id == v.id) {
14358                     cselitem = false;
14359                 }
14360                 return true;
14361             }
14362                 
14363             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14364                 match = this.store.indexOf(v);
14365                 return false;
14366             }
14367             return true;
14368         }, this);
14369         
14370         if (match === false) {
14371             return true; // no more action?
14372         }
14373         // scroll to?
14374         this.view.select(match);
14375         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14376         sn.scrollIntoView(sn.dom.parentNode, false);
14377     },
14378     
14379     onViewScroll : function(e, t){
14380         
14381         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){
14382             return;
14383         }
14384         
14385         this.hasQuery = true;
14386         
14387         this.loading = this.list.select('.loading', true).first();
14388         
14389         if(this.loading === null){
14390             this.list.createChild({
14391                 tag: 'div',
14392                 cls: 'loading roo-select2-more-results roo-select2-active',
14393                 html: 'Loading more results...'
14394             });
14395             
14396             this.loading = this.list.select('.loading', true).first();
14397             
14398             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14399             
14400             this.loading.hide();
14401         }
14402         
14403         this.loading.show();
14404         
14405         var _combo = this;
14406         
14407         this.page++;
14408         this.loadNext = true;
14409         
14410         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14411         
14412         return;
14413     },
14414     
14415     addItem : function(o)
14416     {   
14417         var dv = ''; // display value
14418         
14419         if (this.displayField) {
14420             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14421         } else {
14422             // this is an error condition!!!
14423             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14424         }
14425         
14426         if(!dv.length){
14427             return;
14428         }
14429         
14430         var choice = this.choices.createChild({
14431             tag: 'li',
14432             cls: 'roo-select2-search-choice',
14433             cn: [
14434                 {
14435                     tag: 'div',
14436                     html: dv
14437                 },
14438                 {
14439                     tag: 'a',
14440                     href: '#',
14441                     cls: 'roo-select2-search-choice-close fa fa-times',
14442                     tabindex: '-1'
14443                 }
14444             ]
14445             
14446         }, this.searchField);
14447         
14448         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14449         
14450         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14451         
14452         this.item.push(o);
14453         
14454         this.lastData = o;
14455         
14456         this.syncValue();
14457         
14458         this.inputEl().dom.value = '';
14459         
14460         this.validate();
14461     },
14462     
14463     onRemoveItem : function(e, _self, o)
14464     {
14465         e.preventDefault();
14466         
14467         this.lastItem = Roo.apply([], this.item);
14468         
14469         var index = this.item.indexOf(o.data) * 1;
14470         
14471         if( index < 0){
14472             Roo.log('not this item?!');
14473             return;
14474         }
14475         
14476         this.item.splice(index, 1);
14477         o.item.remove();
14478         
14479         this.syncValue();
14480         
14481         this.fireEvent('remove', this, e);
14482         
14483         this.validate();
14484         
14485     },
14486     
14487     syncValue : function()
14488     {
14489         if(!this.item.length){
14490             this.clearValue();
14491             return;
14492         }
14493             
14494         var value = [];
14495         var _this = this;
14496         Roo.each(this.item, function(i){
14497             if(_this.valueField){
14498                 value.push(i[_this.valueField]);
14499                 return;
14500             }
14501
14502             value.push(i);
14503         });
14504
14505         this.value = value.join(',');
14506
14507         if(this.hiddenField){
14508             this.hiddenField.dom.value = this.value;
14509         }
14510         
14511         this.store.fireEvent("datachanged", this.store);
14512         
14513         this.validate();
14514     },
14515     
14516     clearItem : function()
14517     {
14518         if(!this.multiple){
14519             return;
14520         }
14521         
14522         this.item = [];
14523         
14524         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14525            c.remove();
14526         });
14527         
14528         this.syncValue();
14529         
14530         this.validate();
14531         
14532         if(this.tickable && !Roo.isTouch){
14533             this.view.refresh();
14534         }
14535     },
14536     
14537     inputEl: function ()
14538     {
14539         if(Roo.isIOS && this.useNativeIOS){
14540             return this.el.select('select.roo-ios-select', true).first();
14541         }
14542         
14543         if(Roo.isTouch && this.mobileTouchView){
14544             return this.el.select('input.form-control',true).first();
14545         }
14546         
14547         if(this.tickable){
14548             return this.searchField;
14549         }
14550         
14551         return this.el.select('input.form-control',true).first();
14552     },
14553     
14554     onTickableFooterButtonClick : function(e, btn, el)
14555     {
14556         e.preventDefault();
14557         
14558         this.lastItem = Roo.apply([], this.item);
14559         
14560         if(btn && btn.name == 'cancel'){
14561             this.tickItems = Roo.apply([], this.item);
14562             this.collapse();
14563             return;
14564         }
14565         
14566         this.clearItem();
14567         
14568         var _this = this;
14569         
14570         Roo.each(this.tickItems, function(o){
14571             _this.addItem(o);
14572         });
14573         
14574         this.collapse();
14575         
14576     },
14577     
14578     validate : function()
14579     {
14580         var v = this.getRawValue();
14581         
14582         if(this.multiple){
14583             v = this.getValue();
14584         }
14585         
14586         if(this.disabled || this.allowBlank || v.length){
14587             this.markValid();
14588             return true;
14589         }
14590         
14591         this.markInvalid();
14592         return false;
14593     },
14594     
14595     tickableInputEl : function()
14596     {
14597         if(!this.tickable || !this.editable){
14598             return this.inputEl();
14599         }
14600         
14601         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14602     },
14603     
14604     
14605     getAutoCreateTouchView : function()
14606     {
14607         var id = Roo.id();
14608         
14609         var cfg = {
14610             cls: 'form-group' //input-group
14611         };
14612         
14613         var input =  {
14614             tag: 'input',
14615             id : id,
14616             type : this.inputType,
14617             cls : 'form-control x-combo-noedit',
14618             autocomplete: 'new-password',
14619             placeholder : this.placeholder || '',
14620             readonly : true
14621         };
14622         
14623         if (this.name) {
14624             input.name = this.name;
14625         }
14626         
14627         if (this.size) {
14628             input.cls += ' input-' + this.size;
14629         }
14630         
14631         if (this.disabled) {
14632             input.disabled = true;
14633         }
14634         
14635         var inputblock = {
14636             cls : '',
14637             cn : [
14638                 input
14639             ]
14640         };
14641         
14642         if(this.before){
14643             inputblock.cls += ' input-group';
14644             
14645             inputblock.cn.unshift({
14646                 tag :'span',
14647                 cls : 'input-group-addon',
14648                 html : this.before
14649             });
14650         }
14651         
14652         if(this.removable && !this.multiple){
14653             inputblock.cls += ' roo-removable';
14654             
14655             inputblock.cn.push({
14656                 tag: 'button',
14657                 html : 'x',
14658                 cls : 'roo-combo-removable-btn close'
14659             });
14660         }
14661
14662         if(this.hasFeedback && !this.allowBlank){
14663             
14664             inputblock.cls += ' has-feedback';
14665             
14666             inputblock.cn.push({
14667                 tag: 'span',
14668                 cls: 'glyphicon form-control-feedback'
14669             });
14670             
14671         }
14672         
14673         if (this.after) {
14674             
14675             inputblock.cls += (this.before) ? '' : ' input-group';
14676             
14677             inputblock.cn.push({
14678                 tag :'span',
14679                 cls : 'input-group-addon',
14680                 html : this.after
14681             });
14682         }
14683
14684         var box = {
14685             tag: 'div',
14686             cn: [
14687                 {
14688                     tag: 'input',
14689                     type : 'hidden',
14690                     cls: 'form-hidden-field'
14691                 },
14692                 inputblock
14693             ]
14694             
14695         };
14696         
14697         if(this.multiple){
14698             box = {
14699                 tag: 'div',
14700                 cn: [
14701                     {
14702                         tag: 'input',
14703                         type : 'hidden',
14704                         cls: 'form-hidden-field'
14705                     },
14706                     {
14707                         tag: 'ul',
14708                         cls: 'roo-select2-choices',
14709                         cn:[
14710                             {
14711                                 tag: 'li',
14712                                 cls: 'roo-select2-search-field',
14713                                 cn: [
14714
14715                                     inputblock
14716                                 ]
14717                             }
14718                         ]
14719                     }
14720                 ]
14721             }
14722         };
14723         
14724         var combobox = {
14725             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14726             cn: [
14727                 box
14728             ]
14729         };
14730         
14731         if(!this.multiple && this.showToggleBtn){
14732             
14733             var caret = {
14734                         tag: 'span',
14735                         cls: 'caret'
14736             };
14737             
14738             if (this.caret != false) {
14739                 caret = {
14740                      tag: 'i',
14741                      cls: 'fa fa-' + this.caret
14742                 };
14743                 
14744             }
14745             
14746             combobox.cn.push({
14747                 tag :'span',
14748                 cls : 'input-group-addon btn dropdown-toggle',
14749                 cn : [
14750                     caret,
14751                     {
14752                         tag: 'span',
14753                         cls: 'combobox-clear',
14754                         cn  : [
14755                             {
14756                                 tag : 'i',
14757                                 cls: 'icon-remove'
14758                             }
14759                         ]
14760                     }
14761                 ]
14762
14763             })
14764         }
14765         
14766         if(this.multiple){
14767             combobox.cls += ' roo-select2-container-multi';
14768         }
14769         
14770         var align = this.labelAlign || this.parentLabelAlign();
14771         
14772         if (align ==='left' && this.fieldLabel.length) {
14773
14774             cfg.cn = [
14775                 {
14776                    tag : 'i',
14777                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14778                    tooltip : 'This field is required'
14779                 },
14780                 {
14781                     tag: 'label',
14782                     cls : 'control-label',
14783                     html : this.fieldLabel
14784
14785                 },
14786                 {
14787                     cls : '', 
14788                     cn: [
14789                         combobox
14790                     ]
14791                 }
14792             ];
14793             
14794             var labelCfg = cfg.cn[1];
14795             var contentCfg = cfg.cn[2];
14796             
14797
14798             if(this.indicatorpos == 'right'){
14799                 cfg.cn = [
14800                     {
14801                         tag: 'label',
14802                         'for' :  id,
14803                         cls : 'control-label',
14804                         cn : [
14805                             {
14806                                 tag : 'span',
14807                                 html : this.fieldLabel
14808                             },
14809                             {
14810                                 tag : 'i',
14811                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14812                                 tooltip : 'This field is required'
14813                             }
14814                         ]
14815                     },
14816                     {
14817                         cls : "",
14818                         cn: [
14819                             combobox
14820                         ]
14821                     }
14822
14823                 ];
14824                 
14825                 labelCfg = cfg.cn[0];
14826                 contentCfg = cfg.cn[1];
14827             }
14828             
14829            
14830             
14831             if(this.labelWidth > 12){
14832                 labelCfg.style = "width: " + this.labelWidth + 'px';
14833             }
14834             
14835             if(this.labelWidth < 13 && this.labelmd == 0){
14836                 this.labelmd = this.labelWidth;
14837             }
14838             
14839             if(this.labellg > 0){
14840                 labelCfg.cls += ' col-lg-' + this.labellg;
14841                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14842             }
14843             
14844             if(this.labelmd > 0){
14845                 labelCfg.cls += ' col-md-' + this.labelmd;
14846                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14847             }
14848             
14849             if(this.labelsm > 0){
14850                 labelCfg.cls += ' col-sm-' + this.labelsm;
14851                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14852             }
14853             
14854             if(this.labelxs > 0){
14855                 labelCfg.cls += ' col-xs-' + this.labelxs;
14856                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14857             }
14858                 
14859                 
14860         } else if ( this.fieldLabel.length) {
14861             cfg.cn = [
14862                 {
14863                    tag : 'i',
14864                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14865                    tooltip : 'This field is required'
14866                 },
14867                 {
14868                     tag: 'label',
14869                     cls : 'control-label',
14870                     html : this.fieldLabel
14871
14872                 },
14873                 {
14874                     cls : '', 
14875                     cn: [
14876                         combobox
14877                     ]
14878                 }
14879             ];
14880             
14881             if(this.indicatorpos == 'right'){
14882                 cfg.cn = [
14883                     {
14884                         tag: 'label',
14885                         cls : 'control-label',
14886                         html : this.fieldLabel,
14887                         cn : [
14888                             {
14889                                tag : 'i',
14890                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14891                                tooltip : 'This field is required'
14892                             }
14893                         ]
14894                     },
14895                     {
14896                         cls : '', 
14897                         cn: [
14898                             combobox
14899                         ]
14900                     }
14901                 ];
14902             }
14903         } else {
14904             cfg.cn = combobox;    
14905         }
14906         
14907         
14908         var settings = this;
14909         
14910         ['xs','sm','md','lg'].map(function(size){
14911             if (settings[size]) {
14912                 cfg.cls += ' col-' + size + '-' + settings[size];
14913             }
14914         });
14915         
14916         return cfg;
14917     },
14918     
14919     initTouchView : function()
14920     {
14921         this.renderTouchView();
14922         
14923         this.touchViewEl.on('scroll', function(){
14924             this.el.dom.scrollTop = 0;
14925         }, this);
14926         
14927         this.originalValue = this.getValue();
14928         
14929         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14930         
14931         this.inputEl().on("click", this.showTouchView, this);
14932         if (this.triggerEl) {
14933             this.triggerEl.on("click", this.showTouchView, this);
14934         }
14935         
14936         
14937         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14938         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14939         
14940         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14941         
14942         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14943         this.store.on('load', this.onTouchViewLoad, this);
14944         this.store.on('loadexception', this.onTouchViewLoadException, this);
14945         
14946         if(this.hiddenName){
14947             
14948             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14949             
14950             this.hiddenField.dom.value =
14951                 this.hiddenValue !== undefined ? this.hiddenValue :
14952                 this.value !== undefined ? this.value : '';
14953         
14954             this.el.dom.removeAttribute('name');
14955             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14956         }
14957         
14958         if(this.multiple){
14959             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14960             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14961         }
14962         
14963         if(this.removable && !this.multiple){
14964             var close = this.closeTriggerEl();
14965             if(close){
14966                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14967                 close.on('click', this.removeBtnClick, this, close);
14968             }
14969         }
14970         /*
14971          * fix the bug in Safari iOS8
14972          */
14973         this.inputEl().on("focus", function(e){
14974             document.activeElement.blur();
14975         }, this);
14976         
14977         return;
14978         
14979         
14980     },
14981     
14982     renderTouchView : function()
14983     {
14984         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14985         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14986         
14987         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14988         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14989         
14990         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14991         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14992         this.touchViewBodyEl.setStyle('overflow', 'auto');
14993         
14994         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14995         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14996         
14997         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14998         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14999         
15000     },
15001     
15002     showTouchView : function()
15003     {
15004         if(this.disabled){
15005             return;
15006         }
15007         
15008         this.touchViewHeaderEl.hide();
15009
15010         if(this.modalTitle.length){
15011             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15012             this.touchViewHeaderEl.show();
15013         }
15014
15015         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15016         this.touchViewEl.show();
15017
15018         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15019         
15020         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15021         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15022
15023         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15024
15025         if(this.modalTitle.length){
15026             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15027         }
15028         
15029         this.touchViewBodyEl.setHeight(bodyHeight);
15030
15031         if(this.animate){
15032             var _this = this;
15033             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15034         }else{
15035             this.touchViewEl.addClass('in');
15036         }
15037
15038         this.doTouchViewQuery();
15039         
15040     },
15041     
15042     hideTouchView : function()
15043     {
15044         this.touchViewEl.removeClass('in');
15045
15046         if(this.animate){
15047             var _this = this;
15048             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15049         }else{
15050             this.touchViewEl.setStyle('display', 'none');
15051         }
15052         
15053     },
15054     
15055     setTouchViewValue : function()
15056     {
15057         if(this.multiple){
15058             this.clearItem();
15059         
15060             var _this = this;
15061
15062             Roo.each(this.tickItems, function(o){
15063                 this.addItem(o);
15064             }, this);
15065         }
15066         
15067         this.hideTouchView();
15068     },
15069     
15070     doTouchViewQuery : function()
15071     {
15072         var qe = {
15073             query: '',
15074             forceAll: true,
15075             combo: this,
15076             cancel:false
15077         };
15078         
15079         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15080             return false;
15081         }
15082         
15083         if(!this.alwaysQuery || this.mode == 'local'){
15084             this.onTouchViewLoad();
15085             return;
15086         }
15087         
15088         this.store.load();
15089     },
15090     
15091     onTouchViewBeforeLoad : function(combo,opts)
15092     {
15093         return;
15094     },
15095
15096     // private
15097     onTouchViewLoad : function()
15098     {
15099         if(this.store.getCount() < 1){
15100             this.onTouchViewEmptyResults();
15101             return;
15102         }
15103         
15104         this.clearTouchView();
15105         
15106         var rawValue = this.getRawValue();
15107         
15108         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15109         
15110         this.tickItems = [];
15111         
15112         this.store.data.each(function(d, rowIndex){
15113             var row = this.touchViewListGroup.createChild(template);
15114             
15115             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15116                 row.addClass(d.data.cls);
15117             }
15118             
15119             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15120                 var cfg = {
15121                     data : d.data,
15122                     html : d.data[this.displayField]
15123                 };
15124                 
15125                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15126                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15127                 }
15128             }
15129             row.removeClass('selected');
15130             if(!this.multiple && this.valueField &&
15131                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15132             {
15133                 // radio buttons..
15134                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15135                 row.addClass('selected');
15136             }
15137             
15138             if(this.multiple && this.valueField &&
15139                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15140             {
15141                 
15142                 // checkboxes...
15143                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15144                 this.tickItems.push(d.data);
15145             }
15146             
15147             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15148             
15149         }, this);
15150         
15151         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15152         
15153         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15154
15155         if(this.modalTitle.length){
15156             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15157         }
15158
15159         var listHeight = this.touchViewListGroup.getHeight();
15160         
15161         var _this = this;
15162         
15163         if(firstChecked && listHeight > bodyHeight){
15164             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15165         }
15166         
15167     },
15168     
15169     onTouchViewLoadException : function()
15170     {
15171         this.hideTouchView();
15172     },
15173     
15174     onTouchViewEmptyResults : function()
15175     {
15176         this.clearTouchView();
15177         
15178         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15179         
15180         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15181         
15182     },
15183     
15184     clearTouchView : function()
15185     {
15186         this.touchViewListGroup.dom.innerHTML = '';
15187     },
15188     
15189     onTouchViewClick : function(e, el, o)
15190     {
15191         e.preventDefault();
15192         
15193         var row = o.row;
15194         var rowIndex = o.rowIndex;
15195         
15196         var r = this.store.getAt(rowIndex);
15197         
15198         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15199             
15200             if(!this.multiple){
15201                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15202                     c.dom.removeAttribute('checked');
15203                 }, this);
15204
15205                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15206
15207                 this.setFromData(r.data);
15208
15209                 var close = this.closeTriggerEl();
15210
15211                 if(close){
15212                     close.show();
15213                 }
15214
15215                 this.hideTouchView();
15216
15217                 this.fireEvent('select', this, r, rowIndex);
15218
15219                 return;
15220             }
15221
15222             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15223                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15224                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15225                 return;
15226             }
15227
15228             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15229             this.addItem(r.data);
15230             this.tickItems.push(r.data);
15231         }
15232     },
15233     
15234     getAutoCreateNativeIOS : function()
15235     {
15236         var cfg = {
15237             cls: 'form-group' //input-group,
15238         };
15239         
15240         var combobox =  {
15241             tag: 'select',
15242             cls : 'roo-ios-select'
15243         };
15244         
15245         if (this.name) {
15246             combobox.name = this.name;
15247         }
15248         
15249         if (this.disabled) {
15250             combobox.disabled = true;
15251         }
15252         
15253         var settings = this;
15254         
15255         ['xs','sm','md','lg'].map(function(size){
15256             if (settings[size]) {
15257                 cfg.cls += ' col-' + size + '-' + settings[size];
15258             }
15259         });
15260         
15261         cfg.cn = combobox;
15262         
15263         return cfg;
15264         
15265     },
15266     
15267     initIOSView : function()
15268     {
15269         this.store.on('load', this.onIOSViewLoad, this);
15270         
15271         return;
15272     },
15273     
15274     onIOSViewLoad : function()
15275     {
15276         if(this.store.getCount() < 1){
15277             return;
15278         }
15279         
15280         this.clearIOSView();
15281         
15282         if(this.allowBlank) {
15283             
15284             var default_text = '-- SELECT --';
15285             
15286             if(this.placeholder.length){
15287                 default_text = this.placeholder;
15288             }
15289             
15290             if(this.emptyTitle.length){
15291                 default_text += ' - ' + this.emptyTitle + ' -';
15292             }
15293             
15294             var opt = this.inputEl().createChild({
15295                 tag: 'option',
15296                 value : 0,
15297                 html : default_text
15298             });
15299             
15300             var o = {};
15301             o[this.valueField] = 0;
15302             o[this.displayField] = default_text;
15303             
15304             this.ios_options.push({
15305                 data : o,
15306                 el : opt
15307             });
15308             
15309         }
15310         
15311         this.store.data.each(function(d, rowIndex){
15312             
15313             var html = '';
15314             
15315             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15316                 html = d.data[this.displayField];
15317             }
15318             
15319             var value = '';
15320             
15321             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15322                 value = d.data[this.valueField];
15323             }
15324             
15325             var option = {
15326                 tag: 'option',
15327                 value : value,
15328                 html : html
15329             };
15330             
15331             if(this.value == d.data[this.valueField]){
15332                 option['selected'] = true;
15333             }
15334             
15335             var opt = this.inputEl().createChild(option);
15336             
15337             this.ios_options.push({
15338                 data : d.data,
15339                 el : opt
15340             });
15341             
15342         }, this);
15343         
15344         this.inputEl().on('change', function(){
15345            this.fireEvent('select', this);
15346         }, this);
15347         
15348     },
15349     
15350     clearIOSView: function()
15351     {
15352         this.inputEl().dom.innerHTML = '';
15353         
15354         this.ios_options = [];
15355     },
15356     
15357     setIOSValue: function(v)
15358     {
15359         this.value = v;
15360         
15361         if(!this.ios_options){
15362             return;
15363         }
15364         
15365         Roo.each(this.ios_options, function(opts){
15366            
15367            opts.el.dom.removeAttribute('selected');
15368            
15369            if(opts.data[this.valueField] != v){
15370                return;
15371            }
15372            
15373            opts.el.dom.setAttribute('selected', true);
15374            
15375         }, this);
15376     }
15377
15378     /** 
15379     * @cfg {Boolean} grow 
15380     * @hide 
15381     */
15382     /** 
15383     * @cfg {Number} growMin 
15384     * @hide 
15385     */
15386     /** 
15387     * @cfg {Number} growMax 
15388     * @hide 
15389     */
15390     /**
15391      * @hide
15392      * @method autoSize
15393      */
15394 });
15395
15396 Roo.apply(Roo.bootstrap.ComboBox,  {
15397     
15398     header : {
15399         tag: 'div',
15400         cls: 'modal-header',
15401         cn: [
15402             {
15403                 tag: 'h4',
15404                 cls: 'modal-title'
15405             }
15406         ]
15407     },
15408     
15409     body : {
15410         tag: 'div',
15411         cls: 'modal-body',
15412         cn: [
15413             {
15414                 tag: 'ul',
15415                 cls: 'list-group'
15416             }
15417         ]
15418     },
15419     
15420     listItemRadio : {
15421         tag: 'li',
15422         cls: 'list-group-item',
15423         cn: [
15424             {
15425                 tag: 'span',
15426                 cls: 'roo-combobox-list-group-item-value'
15427             },
15428             {
15429                 tag: 'div',
15430                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15431                 cn: [
15432                     {
15433                         tag: 'input',
15434                         type: 'radio'
15435                     },
15436                     {
15437                         tag: 'label'
15438                     }
15439                 ]
15440             }
15441         ]
15442     },
15443     
15444     listItemCheckbox : {
15445         tag: 'li',
15446         cls: 'list-group-item',
15447         cn: [
15448             {
15449                 tag: 'span',
15450                 cls: 'roo-combobox-list-group-item-value'
15451             },
15452             {
15453                 tag: 'div',
15454                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15455                 cn: [
15456                     {
15457                         tag: 'input',
15458                         type: 'checkbox'
15459                     },
15460                     {
15461                         tag: 'label'
15462                     }
15463                 ]
15464             }
15465         ]
15466     },
15467     
15468     emptyResult : {
15469         tag: 'div',
15470         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15471     },
15472     
15473     footer : {
15474         tag: 'div',
15475         cls: 'modal-footer',
15476         cn: [
15477             {
15478                 tag: 'div',
15479                 cls: 'row',
15480                 cn: [
15481                     {
15482                         tag: 'div',
15483                         cls: 'col-xs-6 text-left',
15484                         cn: {
15485                             tag: 'button',
15486                             cls: 'btn btn-danger roo-touch-view-cancel',
15487                             html: 'Cancel'
15488                         }
15489                     },
15490                     {
15491                         tag: 'div',
15492                         cls: 'col-xs-6 text-right',
15493                         cn: {
15494                             tag: 'button',
15495                             cls: 'btn btn-success roo-touch-view-ok',
15496                             html: 'OK'
15497                         }
15498                     }
15499                 ]
15500             }
15501         ]
15502         
15503     }
15504 });
15505
15506 Roo.apply(Roo.bootstrap.ComboBox,  {
15507     
15508     touchViewTemplate : {
15509         tag: 'div',
15510         cls: 'modal fade roo-combobox-touch-view',
15511         cn: [
15512             {
15513                 tag: 'div',
15514                 cls: 'modal-dialog',
15515                 style : 'position:fixed', // we have to fix position....
15516                 cn: [
15517                     {
15518                         tag: 'div',
15519                         cls: 'modal-content',
15520                         cn: [
15521                             Roo.bootstrap.ComboBox.header,
15522                             Roo.bootstrap.ComboBox.body,
15523                             Roo.bootstrap.ComboBox.footer
15524                         ]
15525                     }
15526                 ]
15527             }
15528         ]
15529     }
15530 });/*
15531  * Based on:
15532  * Ext JS Library 1.1.1
15533  * Copyright(c) 2006-2007, Ext JS, LLC.
15534  *
15535  * Originally Released Under LGPL - original licence link has changed is not relivant.
15536  *
15537  * Fork - LGPL
15538  * <script type="text/javascript">
15539  */
15540
15541 /**
15542  * @class Roo.View
15543  * @extends Roo.util.Observable
15544  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15545  * This class also supports single and multi selection modes. <br>
15546  * Create a data model bound view:
15547  <pre><code>
15548  var store = new Roo.data.Store(...);
15549
15550  var view = new Roo.View({
15551     el : "my-element",
15552     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15553  
15554     singleSelect: true,
15555     selectedClass: "ydataview-selected",
15556     store: store
15557  });
15558
15559  // listen for node click?
15560  view.on("click", function(vw, index, node, e){
15561  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15562  });
15563
15564  // load XML data
15565  dataModel.load("foobar.xml");
15566  </code></pre>
15567  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15568  * <br><br>
15569  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15570  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15571  * 
15572  * Note: old style constructor is still suported (container, template, config)
15573  * 
15574  * @constructor
15575  * Create a new View
15576  * @param {Object} config The config object
15577  * 
15578  */
15579 Roo.View = function(config, depreciated_tpl, depreciated_config){
15580     
15581     this.parent = false;
15582     
15583     if (typeof(depreciated_tpl) == 'undefined') {
15584         // new way.. - universal constructor.
15585         Roo.apply(this, config);
15586         this.el  = Roo.get(this.el);
15587     } else {
15588         // old format..
15589         this.el  = Roo.get(config);
15590         this.tpl = depreciated_tpl;
15591         Roo.apply(this, depreciated_config);
15592     }
15593     this.wrapEl  = this.el.wrap().wrap();
15594     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15595     
15596     
15597     if(typeof(this.tpl) == "string"){
15598         this.tpl = new Roo.Template(this.tpl);
15599     } else {
15600         // support xtype ctors..
15601         this.tpl = new Roo.factory(this.tpl, Roo);
15602     }
15603     
15604     
15605     this.tpl.compile();
15606     
15607     /** @private */
15608     this.addEvents({
15609         /**
15610          * @event beforeclick
15611          * Fires before a click is processed. Returns false to cancel the default action.
15612          * @param {Roo.View} this
15613          * @param {Number} index The index of the target node
15614          * @param {HTMLElement} node The target node
15615          * @param {Roo.EventObject} e The raw event object
15616          */
15617             "beforeclick" : true,
15618         /**
15619          * @event click
15620          * Fires when a template node is clicked.
15621          * @param {Roo.View} this
15622          * @param {Number} index The index of the target node
15623          * @param {HTMLElement} node The target node
15624          * @param {Roo.EventObject} e The raw event object
15625          */
15626             "click" : true,
15627         /**
15628          * @event dblclick
15629          * Fires when a template node is double clicked.
15630          * @param {Roo.View} this
15631          * @param {Number} index The index of the target node
15632          * @param {HTMLElement} node The target node
15633          * @param {Roo.EventObject} e The raw event object
15634          */
15635             "dblclick" : true,
15636         /**
15637          * @event contextmenu
15638          * Fires when a template node is right clicked.
15639          * @param {Roo.View} this
15640          * @param {Number} index The index of the target node
15641          * @param {HTMLElement} node The target node
15642          * @param {Roo.EventObject} e The raw event object
15643          */
15644             "contextmenu" : true,
15645         /**
15646          * @event selectionchange
15647          * Fires when the selected nodes change.
15648          * @param {Roo.View} this
15649          * @param {Array} selections Array of the selected nodes
15650          */
15651             "selectionchange" : true,
15652     
15653         /**
15654          * @event beforeselect
15655          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15656          * @param {Roo.View} this
15657          * @param {HTMLElement} node The node to be selected
15658          * @param {Array} selections Array of currently selected nodes
15659          */
15660             "beforeselect" : true,
15661         /**
15662          * @event preparedata
15663          * Fires on every row to render, to allow you to change the data.
15664          * @param {Roo.View} this
15665          * @param {Object} data to be rendered (change this)
15666          */
15667           "preparedata" : true
15668           
15669           
15670         });
15671
15672
15673
15674     this.el.on({
15675         "click": this.onClick,
15676         "dblclick": this.onDblClick,
15677         "contextmenu": this.onContextMenu,
15678         scope:this
15679     });
15680
15681     this.selections = [];
15682     this.nodes = [];
15683     this.cmp = new Roo.CompositeElementLite([]);
15684     if(this.store){
15685         this.store = Roo.factory(this.store, Roo.data);
15686         this.setStore(this.store, true);
15687     }
15688     
15689     if ( this.footer && this.footer.xtype) {
15690            
15691          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15692         
15693         this.footer.dataSource = this.store;
15694         this.footer.container = fctr;
15695         this.footer = Roo.factory(this.footer, Roo);
15696         fctr.insertFirst(this.el);
15697         
15698         // this is a bit insane - as the paging toolbar seems to detach the el..
15699 //        dom.parentNode.parentNode.parentNode
15700          // they get detached?
15701     }
15702     
15703     
15704     Roo.View.superclass.constructor.call(this);
15705     
15706     
15707 };
15708
15709 Roo.extend(Roo.View, Roo.util.Observable, {
15710     
15711      /**
15712      * @cfg {Roo.data.Store} store Data store to load data from.
15713      */
15714     store : false,
15715     
15716     /**
15717      * @cfg {String|Roo.Element} el The container element.
15718      */
15719     el : '',
15720     
15721     /**
15722      * @cfg {String|Roo.Template} tpl The template used by this View 
15723      */
15724     tpl : false,
15725     /**
15726      * @cfg {String} dataName the named area of the template to use as the data area
15727      *                          Works with domtemplates roo-name="name"
15728      */
15729     dataName: false,
15730     /**
15731      * @cfg {String} selectedClass The css class to add to selected nodes
15732      */
15733     selectedClass : "x-view-selected",
15734      /**
15735      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15736      */
15737     emptyText : "",
15738     
15739     /**
15740      * @cfg {String} text to display on mask (default Loading)
15741      */
15742     mask : false,
15743     /**
15744      * @cfg {Boolean} multiSelect Allow multiple selection
15745      */
15746     multiSelect : false,
15747     /**
15748      * @cfg {Boolean} singleSelect Allow single selection
15749      */
15750     singleSelect:  false,
15751     
15752     /**
15753      * @cfg {Boolean} toggleSelect - selecting 
15754      */
15755     toggleSelect : false,
15756     
15757     /**
15758      * @cfg {Boolean} tickable - selecting 
15759      */
15760     tickable : false,
15761     
15762     /**
15763      * Returns the element this view is bound to.
15764      * @return {Roo.Element}
15765      */
15766     getEl : function(){
15767         return this.wrapEl;
15768     },
15769     
15770     
15771
15772     /**
15773      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15774      */
15775     refresh : function(){
15776         //Roo.log('refresh');
15777         var t = this.tpl;
15778         
15779         // if we are using something like 'domtemplate', then
15780         // the what gets used is:
15781         // t.applySubtemplate(NAME, data, wrapping data..)
15782         // the outer template then get' applied with
15783         //     the store 'extra data'
15784         // and the body get's added to the
15785         //      roo-name="data" node?
15786         //      <span class='roo-tpl-{name}'></span> ?????
15787         
15788         
15789         
15790         this.clearSelections();
15791         this.el.update("");
15792         var html = [];
15793         var records = this.store.getRange();
15794         if(records.length < 1) {
15795             
15796             // is this valid??  = should it render a template??
15797             
15798             this.el.update(this.emptyText);
15799             return;
15800         }
15801         var el = this.el;
15802         if (this.dataName) {
15803             this.el.update(t.apply(this.store.meta)); //????
15804             el = this.el.child('.roo-tpl-' + this.dataName);
15805         }
15806         
15807         for(var i = 0, len = records.length; i < len; i++){
15808             var data = this.prepareData(records[i].data, i, records[i]);
15809             this.fireEvent("preparedata", this, data, i, records[i]);
15810             
15811             var d = Roo.apply({}, data);
15812             
15813             if(this.tickable){
15814                 Roo.apply(d, {'roo-id' : Roo.id()});
15815                 
15816                 var _this = this;
15817             
15818                 Roo.each(this.parent.item, function(item){
15819                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15820                         return;
15821                     }
15822                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15823                 });
15824             }
15825             
15826             html[html.length] = Roo.util.Format.trim(
15827                 this.dataName ?
15828                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15829                     t.apply(d)
15830             );
15831         }
15832         
15833         
15834         
15835         el.update(html.join(""));
15836         this.nodes = el.dom.childNodes;
15837         this.updateIndexes(0);
15838     },
15839     
15840
15841     /**
15842      * Function to override to reformat the data that is sent to
15843      * the template for each node.
15844      * DEPRICATED - use the preparedata event handler.
15845      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15846      * a JSON object for an UpdateManager bound view).
15847      */
15848     prepareData : function(data, index, record)
15849     {
15850         this.fireEvent("preparedata", this, data, index, record);
15851         return data;
15852     },
15853
15854     onUpdate : function(ds, record){
15855         // Roo.log('on update');   
15856         this.clearSelections();
15857         var index = this.store.indexOf(record);
15858         var n = this.nodes[index];
15859         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15860         n.parentNode.removeChild(n);
15861         this.updateIndexes(index, index);
15862     },
15863
15864     
15865     
15866 // --------- FIXME     
15867     onAdd : function(ds, records, index)
15868     {
15869         //Roo.log(['on Add', ds, records, index] );        
15870         this.clearSelections();
15871         if(this.nodes.length == 0){
15872             this.refresh();
15873             return;
15874         }
15875         var n = this.nodes[index];
15876         for(var i = 0, len = records.length; i < len; i++){
15877             var d = this.prepareData(records[i].data, i, records[i]);
15878             if(n){
15879                 this.tpl.insertBefore(n, d);
15880             }else{
15881                 
15882                 this.tpl.append(this.el, d);
15883             }
15884         }
15885         this.updateIndexes(index);
15886     },
15887
15888     onRemove : function(ds, record, index){
15889        // Roo.log('onRemove');
15890         this.clearSelections();
15891         var el = this.dataName  ?
15892             this.el.child('.roo-tpl-' + this.dataName) :
15893             this.el; 
15894         
15895         el.dom.removeChild(this.nodes[index]);
15896         this.updateIndexes(index);
15897     },
15898
15899     /**
15900      * Refresh an individual node.
15901      * @param {Number} index
15902      */
15903     refreshNode : function(index){
15904         this.onUpdate(this.store, this.store.getAt(index));
15905     },
15906
15907     updateIndexes : function(startIndex, endIndex){
15908         var ns = this.nodes;
15909         startIndex = startIndex || 0;
15910         endIndex = endIndex || ns.length - 1;
15911         for(var i = startIndex; i <= endIndex; i++){
15912             ns[i].nodeIndex = i;
15913         }
15914     },
15915
15916     /**
15917      * Changes the data store this view uses and refresh the view.
15918      * @param {Store} store
15919      */
15920     setStore : function(store, initial){
15921         if(!initial && this.store){
15922             this.store.un("datachanged", this.refresh);
15923             this.store.un("add", this.onAdd);
15924             this.store.un("remove", this.onRemove);
15925             this.store.un("update", this.onUpdate);
15926             this.store.un("clear", this.refresh);
15927             this.store.un("beforeload", this.onBeforeLoad);
15928             this.store.un("load", this.onLoad);
15929             this.store.un("loadexception", this.onLoad);
15930         }
15931         if(store){
15932           
15933             store.on("datachanged", this.refresh, this);
15934             store.on("add", this.onAdd, this);
15935             store.on("remove", this.onRemove, this);
15936             store.on("update", this.onUpdate, this);
15937             store.on("clear", this.refresh, this);
15938             store.on("beforeload", this.onBeforeLoad, this);
15939             store.on("load", this.onLoad, this);
15940             store.on("loadexception", this.onLoad, this);
15941         }
15942         
15943         if(store){
15944             this.refresh();
15945         }
15946     },
15947     /**
15948      * onbeforeLoad - masks the loading area.
15949      *
15950      */
15951     onBeforeLoad : function(store,opts)
15952     {
15953          //Roo.log('onBeforeLoad');   
15954         if (!opts.add) {
15955             this.el.update("");
15956         }
15957         this.el.mask(this.mask ? this.mask : "Loading" ); 
15958     },
15959     onLoad : function ()
15960     {
15961         this.el.unmask();
15962     },
15963     
15964
15965     /**
15966      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15967      * @param {HTMLElement} node
15968      * @return {HTMLElement} The template node
15969      */
15970     findItemFromChild : function(node){
15971         var el = this.dataName  ?
15972             this.el.child('.roo-tpl-' + this.dataName,true) :
15973             this.el.dom; 
15974         
15975         if(!node || node.parentNode == el){
15976                     return node;
15977             }
15978             var p = node.parentNode;
15979             while(p && p != el){
15980             if(p.parentNode == el){
15981                 return p;
15982             }
15983             p = p.parentNode;
15984         }
15985             return null;
15986     },
15987
15988     /** @ignore */
15989     onClick : function(e){
15990         var item = this.findItemFromChild(e.getTarget());
15991         if(item){
15992             var index = this.indexOf(item);
15993             if(this.onItemClick(item, index, e) !== false){
15994                 this.fireEvent("click", this, index, item, e);
15995             }
15996         }else{
15997             this.clearSelections();
15998         }
15999     },
16000
16001     /** @ignore */
16002     onContextMenu : function(e){
16003         var item = this.findItemFromChild(e.getTarget());
16004         if(item){
16005             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16006         }
16007     },
16008
16009     /** @ignore */
16010     onDblClick : function(e){
16011         var item = this.findItemFromChild(e.getTarget());
16012         if(item){
16013             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16014         }
16015     },
16016
16017     onItemClick : function(item, index, e)
16018     {
16019         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16020             return false;
16021         }
16022         if (this.toggleSelect) {
16023             var m = this.isSelected(item) ? 'unselect' : 'select';
16024             //Roo.log(m);
16025             var _t = this;
16026             _t[m](item, true, false);
16027             return true;
16028         }
16029         if(this.multiSelect || this.singleSelect){
16030             if(this.multiSelect && e.shiftKey && this.lastSelection){
16031                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16032             }else{
16033                 this.select(item, this.multiSelect && e.ctrlKey);
16034                 this.lastSelection = item;
16035             }
16036             
16037             if(!this.tickable){
16038                 e.preventDefault();
16039             }
16040             
16041         }
16042         return true;
16043     },
16044
16045     /**
16046      * Get the number of selected nodes.
16047      * @return {Number}
16048      */
16049     getSelectionCount : function(){
16050         return this.selections.length;
16051     },
16052
16053     /**
16054      * Get the currently selected nodes.
16055      * @return {Array} An array of HTMLElements
16056      */
16057     getSelectedNodes : function(){
16058         return this.selections;
16059     },
16060
16061     /**
16062      * Get the indexes of the selected nodes.
16063      * @return {Array}
16064      */
16065     getSelectedIndexes : function(){
16066         var indexes = [], s = this.selections;
16067         for(var i = 0, len = s.length; i < len; i++){
16068             indexes.push(s[i].nodeIndex);
16069         }
16070         return indexes;
16071     },
16072
16073     /**
16074      * Clear all selections
16075      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16076      */
16077     clearSelections : function(suppressEvent){
16078         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16079             this.cmp.elements = this.selections;
16080             this.cmp.removeClass(this.selectedClass);
16081             this.selections = [];
16082             if(!suppressEvent){
16083                 this.fireEvent("selectionchange", this, this.selections);
16084             }
16085         }
16086     },
16087
16088     /**
16089      * Returns true if the passed node is selected
16090      * @param {HTMLElement/Number} node The node or node index
16091      * @return {Boolean}
16092      */
16093     isSelected : function(node){
16094         var s = this.selections;
16095         if(s.length < 1){
16096             return false;
16097         }
16098         node = this.getNode(node);
16099         return s.indexOf(node) !== -1;
16100     },
16101
16102     /**
16103      * Selects nodes.
16104      * @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
16105      * @param {Boolean} keepExisting (optional) true to keep existing selections
16106      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16107      */
16108     select : function(nodeInfo, keepExisting, suppressEvent){
16109         if(nodeInfo instanceof Array){
16110             if(!keepExisting){
16111                 this.clearSelections(true);
16112             }
16113             for(var i = 0, len = nodeInfo.length; i < len; i++){
16114                 this.select(nodeInfo[i], true, true);
16115             }
16116             return;
16117         } 
16118         var node = this.getNode(nodeInfo);
16119         if(!node || this.isSelected(node)){
16120             return; // already selected.
16121         }
16122         if(!keepExisting){
16123             this.clearSelections(true);
16124         }
16125         
16126         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16127             Roo.fly(node).addClass(this.selectedClass);
16128             this.selections.push(node);
16129             if(!suppressEvent){
16130                 this.fireEvent("selectionchange", this, this.selections);
16131             }
16132         }
16133         
16134         
16135     },
16136       /**
16137      * Unselects nodes.
16138      * @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
16139      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16140      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16141      */
16142     unselect : function(nodeInfo, keepExisting, suppressEvent)
16143     {
16144         if(nodeInfo instanceof Array){
16145             Roo.each(this.selections, function(s) {
16146                 this.unselect(s, nodeInfo);
16147             }, this);
16148             return;
16149         }
16150         var node = this.getNode(nodeInfo);
16151         if(!node || !this.isSelected(node)){
16152             //Roo.log("not selected");
16153             return; // not selected.
16154         }
16155         // fireevent???
16156         var ns = [];
16157         Roo.each(this.selections, function(s) {
16158             if (s == node ) {
16159                 Roo.fly(node).removeClass(this.selectedClass);
16160
16161                 return;
16162             }
16163             ns.push(s);
16164         },this);
16165         
16166         this.selections= ns;
16167         this.fireEvent("selectionchange", this, this.selections);
16168     },
16169
16170     /**
16171      * Gets a template node.
16172      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16173      * @return {HTMLElement} The node or null if it wasn't found
16174      */
16175     getNode : function(nodeInfo){
16176         if(typeof nodeInfo == "string"){
16177             return document.getElementById(nodeInfo);
16178         }else if(typeof nodeInfo == "number"){
16179             return this.nodes[nodeInfo];
16180         }
16181         return nodeInfo;
16182     },
16183
16184     /**
16185      * Gets a range template nodes.
16186      * @param {Number} startIndex
16187      * @param {Number} endIndex
16188      * @return {Array} An array of nodes
16189      */
16190     getNodes : function(start, end){
16191         var ns = this.nodes;
16192         start = start || 0;
16193         end = typeof end == "undefined" ? ns.length - 1 : end;
16194         var nodes = [];
16195         if(start <= end){
16196             for(var i = start; i <= end; i++){
16197                 nodes.push(ns[i]);
16198             }
16199         } else{
16200             for(var i = start; i >= end; i--){
16201                 nodes.push(ns[i]);
16202             }
16203         }
16204         return nodes;
16205     },
16206
16207     /**
16208      * Finds the index of the passed node
16209      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16210      * @return {Number} The index of the node or -1
16211      */
16212     indexOf : function(node){
16213         node = this.getNode(node);
16214         if(typeof node.nodeIndex == "number"){
16215             return node.nodeIndex;
16216         }
16217         var ns = this.nodes;
16218         for(var i = 0, len = ns.length; i < len; i++){
16219             if(ns[i] == node){
16220                 return i;
16221             }
16222         }
16223         return -1;
16224     }
16225 });
16226 /*
16227  * - LGPL
16228  *
16229  * based on jquery fullcalendar
16230  * 
16231  */
16232
16233 Roo.bootstrap = Roo.bootstrap || {};
16234 /**
16235  * @class Roo.bootstrap.Calendar
16236  * @extends Roo.bootstrap.Component
16237  * Bootstrap Calendar class
16238  * @cfg {Boolean} loadMask (true|false) default false
16239  * @cfg {Object} header generate the user specific header of the calendar, default false
16240
16241  * @constructor
16242  * Create a new Container
16243  * @param {Object} config The config object
16244  */
16245
16246
16247
16248 Roo.bootstrap.Calendar = function(config){
16249     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16250      this.addEvents({
16251         /**
16252              * @event select
16253              * Fires when a date is selected
16254              * @param {DatePicker} this
16255              * @param {Date} date The selected date
16256              */
16257         'select': true,
16258         /**
16259              * @event monthchange
16260              * Fires when the displayed month changes 
16261              * @param {DatePicker} this
16262              * @param {Date} date The selected month
16263              */
16264         'monthchange': true,
16265         /**
16266              * @event evententer
16267              * Fires when mouse over an event
16268              * @param {Calendar} this
16269              * @param {event} Event
16270              */
16271         'evententer': true,
16272         /**
16273              * @event eventleave
16274              * Fires when the mouse leaves an
16275              * @param {Calendar} this
16276              * @param {event}
16277              */
16278         'eventleave': true,
16279         /**
16280              * @event eventclick
16281              * Fires when the mouse click an
16282              * @param {Calendar} this
16283              * @param {event}
16284              */
16285         'eventclick': true
16286         
16287     });
16288
16289 };
16290
16291 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16292     
16293      /**
16294      * @cfg {Number} startDay
16295      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16296      */
16297     startDay : 0,
16298     
16299     loadMask : false,
16300     
16301     header : false,
16302       
16303     getAutoCreate : function(){
16304         
16305         
16306         var fc_button = function(name, corner, style, content ) {
16307             return Roo.apply({},{
16308                 tag : 'span',
16309                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16310                          (corner.length ?
16311                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16312                             ''
16313                         ),
16314                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16315                 unselectable: 'on'
16316             });
16317         };
16318         
16319         var header = {};
16320         
16321         if(!this.header){
16322             header = {
16323                 tag : 'table',
16324                 cls : 'fc-header',
16325                 style : 'width:100%',
16326                 cn : [
16327                     {
16328                         tag: 'tr',
16329                         cn : [
16330                             {
16331                                 tag : 'td',
16332                                 cls : 'fc-header-left',
16333                                 cn : [
16334                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16335                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16336                                     { tag: 'span', cls: 'fc-header-space' },
16337                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16338
16339
16340                                 ]
16341                             },
16342
16343                             {
16344                                 tag : 'td',
16345                                 cls : 'fc-header-center',
16346                                 cn : [
16347                                     {
16348                                         tag: 'span',
16349                                         cls: 'fc-header-title',
16350                                         cn : {
16351                                             tag: 'H2',
16352                                             html : 'month / year'
16353                                         }
16354                                     }
16355
16356                                 ]
16357                             },
16358                             {
16359                                 tag : 'td',
16360                                 cls : 'fc-header-right',
16361                                 cn : [
16362                               /*      fc_button('month', 'left', '', 'month' ),
16363                                     fc_button('week', '', '', 'week' ),
16364                                     fc_button('day', 'right', '', 'day' )
16365                                 */    
16366
16367                                 ]
16368                             }
16369
16370                         ]
16371                     }
16372                 ]
16373             };
16374         }
16375         
16376         header = this.header;
16377         
16378        
16379         var cal_heads = function() {
16380             var ret = [];
16381             // fixme - handle this.
16382             
16383             for (var i =0; i < Date.dayNames.length; i++) {
16384                 var d = Date.dayNames[i];
16385                 ret.push({
16386                     tag: 'th',
16387                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16388                     html : d.substring(0,3)
16389                 });
16390                 
16391             }
16392             ret[0].cls += ' fc-first';
16393             ret[6].cls += ' fc-last';
16394             return ret;
16395         };
16396         var cal_cell = function(n) {
16397             return  {
16398                 tag: 'td',
16399                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16400                 cn : [
16401                     {
16402                         cn : [
16403                             {
16404                                 cls: 'fc-day-number',
16405                                 html: 'D'
16406                             },
16407                             {
16408                                 cls: 'fc-day-content',
16409                              
16410                                 cn : [
16411                                      {
16412                                         style: 'position: relative;' // height: 17px;
16413                                     }
16414                                 ]
16415                             }
16416                             
16417                             
16418                         ]
16419                     }
16420                 ]
16421                 
16422             }
16423         };
16424         var cal_rows = function() {
16425             
16426             var ret = [];
16427             for (var r = 0; r < 6; r++) {
16428                 var row= {
16429                     tag : 'tr',
16430                     cls : 'fc-week',
16431                     cn : []
16432                 };
16433                 
16434                 for (var i =0; i < Date.dayNames.length; i++) {
16435                     var d = Date.dayNames[i];
16436                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16437
16438                 }
16439                 row.cn[0].cls+=' fc-first';
16440                 row.cn[0].cn[0].style = 'min-height:90px';
16441                 row.cn[6].cls+=' fc-last';
16442                 ret.push(row);
16443                 
16444             }
16445             ret[0].cls += ' fc-first';
16446             ret[4].cls += ' fc-prev-last';
16447             ret[5].cls += ' fc-last';
16448             return ret;
16449             
16450         };
16451         
16452         var cal_table = {
16453             tag: 'table',
16454             cls: 'fc-border-separate',
16455             style : 'width:100%',
16456             cellspacing  : 0,
16457             cn : [
16458                 { 
16459                     tag: 'thead',
16460                     cn : [
16461                         { 
16462                             tag: 'tr',
16463                             cls : 'fc-first fc-last',
16464                             cn : cal_heads()
16465                         }
16466                     ]
16467                 },
16468                 { 
16469                     tag: 'tbody',
16470                     cn : cal_rows()
16471                 }
16472                   
16473             ]
16474         };
16475          
16476          var cfg = {
16477             cls : 'fc fc-ltr',
16478             cn : [
16479                 header,
16480                 {
16481                     cls : 'fc-content',
16482                     style : "position: relative;",
16483                     cn : [
16484                         {
16485                             cls : 'fc-view fc-view-month fc-grid',
16486                             style : 'position: relative',
16487                             unselectable : 'on',
16488                             cn : [
16489                                 {
16490                                     cls : 'fc-event-container',
16491                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16492                                 },
16493                                 cal_table
16494                             ]
16495                         }
16496                     ]
16497     
16498                 }
16499            ] 
16500             
16501         };
16502         
16503          
16504         
16505         return cfg;
16506     },
16507     
16508     
16509     initEvents : function()
16510     {
16511         if(!this.store){
16512             throw "can not find store for calendar";
16513         }
16514         
16515         var mark = {
16516             tag: "div",
16517             cls:"x-dlg-mask",
16518             style: "text-align:center",
16519             cn: [
16520                 {
16521                     tag: "div",
16522                     style: "background-color:white;width:50%;margin:250 auto",
16523                     cn: [
16524                         {
16525                             tag: "img",
16526                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16527                         },
16528                         {
16529                             tag: "span",
16530                             html: "Loading"
16531                         }
16532                         
16533                     ]
16534                 }
16535             ]
16536         };
16537         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16538         
16539         var size = this.el.select('.fc-content', true).first().getSize();
16540         this.maskEl.setSize(size.width, size.height);
16541         this.maskEl.enableDisplayMode("block");
16542         if(!this.loadMask){
16543             this.maskEl.hide();
16544         }
16545         
16546         this.store = Roo.factory(this.store, Roo.data);
16547         this.store.on('load', this.onLoad, this);
16548         this.store.on('beforeload', this.onBeforeLoad, this);
16549         
16550         this.resize();
16551         
16552         this.cells = this.el.select('.fc-day',true);
16553         //Roo.log(this.cells);
16554         this.textNodes = this.el.query('.fc-day-number');
16555         this.cells.addClassOnOver('fc-state-hover');
16556         
16557         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16558         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16559         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16560         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16561         
16562         this.on('monthchange', this.onMonthChange, this);
16563         
16564         this.update(new Date().clearTime());
16565     },
16566     
16567     resize : function() {
16568         var sz  = this.el.getSize();
16569         
16570         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16571         this.el.select('.fc-day-content div',true).setHeight(34);
16572     },
16573     
16574     
16575     // private
16576     showPrevMonth : function(e){
16577         this.update(this.activeDate.add("mo", -1));
16578     },
16579     showToday : function(e){
16580         this.update(new Date().clearTime());
16581     },
16582     // private
16583     showNextMonth : function(e){
16584         this.update(this.activeDate.add("mo", 1));
16585     },
16586
16587     // private
16588     showPrevYear : function(){
16589         this.update(this.activeDate.add("y", -1));
16590     },
16591
16592     // private
16593     showNextYear : function(){
16594         this.update(this.activeDate.add("y", 1));
16595     },
16596
16597     
16598    // private
16599     update : function(date)
16600     {
16601         var vd = this.activeDate;
16602         this.activeDate = date;
16603 //        if(vd && this.el){
16604 //            var t = date.getTime();
16605 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16606 //                Roo.log('using add remove');
16607 //                
16608 //                this.fireEvent('monthchange', this, date);
16609 //                
16610 //                this.cells.removeClass("fc-state-highlight");
16611 //                this.cells.each(function(c){
16612 //                   if(c.dateValue == t){
16613 //                       c.addClass("fc-state-highlight");
16614 //                       setTimeout(function(){
16615 //                            try{c.dom.firstChild.focus();}catch(e){}
16616 //                       }, 50);
16617 //                       return false;
16618 //                   }
16619 //                   return true;
16620 //                });
16621 //                return;
16622 //            }
16623 //        }
16624         
16625         var days = date.getDaysInMonth();
16626         
16627         var firstOfMonth = date.getFirstDateOfMonth();
16628         var startingPos = firstOfMonth.getDay()-this.startDay;
16629         
16630         if(startingPos < this.startDay){
16631             startingPos += 7;
16632         }
16633         
16634         var pm = date.add(Date.MONTH, -1);
16635         var prevStart = pm.getDaysInMonth()-startingPos;
16636 //        
16637         this.cells = this.el.select('.fc-day',true);
16638         this.textNodes = this.el.query('.fc-day-number');
16639         this.cells.addClassOnOver('fc-state-hover');
16640         
16641         var cells = this.cells.elements;
16642         var textEls = this.textNodes;
16643         
16644         Roo.each(cells, function(cell){
16645             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16646         });
16647         
16648         days += startingPos;
16649
16650         // convert everything to numbers so it's fast
16651         var day = 86400000;
16652         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16653         //Roo.log(d);
16654         //Roo.log(pm);
16655         //Roo.log(prevStart);
16656         
16657         var today = new Date().clearTime().getTime();
16658         var sel = date.clearTime().getTime();
16659         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16660         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16661         var ddMatch = this.disabledDatesRE;
16662         var ddText = this.disabledDatesText;
16663         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16664         var ddaysText = this.disabledDaysText;
16665         var format = this.format;
16666         
16667         var setCellClass = function(cal, cell){
16668             cell.row = 0;
16669             cell.events = [];
16670             cell.more = [];
16671             //Roo.log('set Cell Class');
16672             cell.title = "";
16673             var t = d.getTime();
16674             
16675             //Roo.log(d);
16676             
16677             cell.dateValue = t;
16678             if(t == today){
16679                 cell.className += " fc-today";
16680                 cell.className += " fc-state-highlight";
16681                 cell.title = cal.todayText;
16682             }
16683             if(t == sel){
16684                 // disable highlight in other month..
16685                 //cell.className += " fc-state-highlight";
16686                 
16687             }
16688             // disabling
16689             if(t < min) {
16690                 cell.className = " fc-state-disabled";
16691                 cell.title = cal.minText;
16692                 return;
16693             }
16694             if(t > max) {
16695                 cell.className = " fc-state-disabled";
16696                 cell.title = cal.maxText;
16697                 return;
16698             }
16699             if(ddays){
16700                 if(ddays.indexOf(d.getDay()) != -1){
16701                     cell.title = ddaysText;
16702                     cell.className = " fc-state-disabled";
16703                 }
16704             }
16705             if(ddMatch && format){
16706                 var fvalue = d.dateFormat(format);
16707                 if(ddMatch.test(fvalue)){
16708                     cell.title = ddText.replace("%0", fvalue);
16709                     cell.className = " fc-state-disabled";
16710                 }
16711             }
16712             
16713             if (!cell.initialClassName) {
16714                 cell.initialClassName = cell.dom.className;
16715             }
16716             
16717             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16718         };
16719
16720         var i = 0;
16721         
16722         for(; i < startingPos; i++) {
16723             textEls[i].innerHTML = (++prevStart);
16724             d.setDate(d.getDate()+1);
16725             
16726             cells[i].className = "fc-past fc-other-month";
16727             setCellClass(this, cells[i]);
16728         }
16729         
16730         var intDay = 0;
16731         
16732         for(; i < days; i++){
16733             intDay = i - startingPos + 1;
16734             textEls[i].innerHTML = (intDay);
16735             d.setDate(d.getDate()+1);
16736             
16737             cells[i].className = ''; // "x-date-active";
16738             setCellClass(this, cells[i]);
16739         }
16740         var extraDays = 0;
16741         
16742         for(; i < 42; i++) {
16743             textEls[i].innerHTML = (++extraDays);
16744             d.setDate(d.getDate()+1);
16745             
16746             cells[i].className = "fc-future fc-other-month";
16747             setCellClass(this, cells[i]);
16748         }
16749         
16750         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16751         
16752         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16753         
16754         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16755         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16756         
16757         if(totalRows != 6){
16758             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16759             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16760         }
16761         
16762         this.fireEvent('monthchange', this, date);
16763         
16764         
16765         /*
16766         if(!this.internalRender){
16767             var main = this.el.dom.firstChild;
16768             var w = main.offsetWidth;
16769             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16770             Roo.fly(main).setWidth(w);
16771             this.internalRender = true;
16772             // opera does not respect the auto grow header center column
16773             // then, after it gets a width opera refuses to recalculate
16774             // without a second pass
16775             if(Roo.isOpera && !this.secondPass){
16776                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16777                 this.secondPass = true;
16778                 this.update.defer(10, this, [date]);
16779             }
16780         }
16781         */
16782         
16783     },
16784     
16785     findCell : function(dt) {
16786         dt = dt.clearTime().getTime();
16787         var ret = false;
16788         this.cells.each(function(c){
16789             //Roo.log("check " +c.dateValue + '?=' + dt);
16790             if(c.dateValue == dt){
16791                 ret = c;
16792                 return false;
16793             }
16794             return true;
16795         });
16796         
16797         return ret;
16798     },
16799     
16800     findCells : function(ev) {
16801         var s = ev.start.clone().clearTime().getTime();
16802        // Roo.log(s);
16803         var e= ev.end.clone().clearTime().getTime();
16804        // Roo.log(e);
16805         var ret = [];
16806         this.cells.each(function(c){
16807              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16808             
16809             if(c.dateValue > e){
16810                 return ;
16811             }
16812             if(c.dateValue < s){
16813                 return ;
16814             }
16815             ret.push(c);
16816         });
16817         
16818         return ret;    
16819     },
16820     
16821 //    findBestRow: function(cells)
16822 //    {
16823 //        var ret = 0;
16824 //        
16825 //        for (var i =0 ; i < cells.length;i++) {
16826 //            ret  = Math.max(cells[i].rows || 0,ret);
16827 //        }
16828 //        return ret;
16829 //        
16830 //    },
16831     
16832     
16833     addItem : function(ev)
16834     {
16835         // look for vertical location slot in
16836         var cells = this.findCells(ev);
16837         
16838 //        ev.row = this.findBestRow(cells);
16839         
16840         // work out the location.
16841         
16842         var crow = false;
16843         var rows = [];
16844         for(var i =0; i < cells.length; i++) {
16845             
16846             cells[i].row = cells[0].row;
16847             
16848             if(i == 0){
16849                 cells[i].row = cells[i].row + 1;
16850             }
16851             
16852             if (!crow) {
16853                 crow = {
16854                     start : cells[i],
16855                     end :  cells[i]
16856                 };
16857                 continue;
16858             }
16859             if (crow.start.getY() == cells[i].getY()) {
16860                 // on same row.
16861                 crow.end = cells[i];
16862                 continue;
16863             }
16864             // different row.
16865             rows.push(crow);
16866             crow = {
16867                 start: cells[i],
16868                 end : cells[i]
16869             };
16870             
16871         }
16872         
16873         rows.push(crow);
16874         ev.els = [];
16875         ev.rows = rows;
16876         ev.cells = cells;
16877         
16878         cells[0].events.push(ev);
16879         
16880         this.calevents.push(ev);
16881     },
16882     
16883     clearEvents: function() {
16884         
16885         if(!this.calevents){
16886             return;
16887         }
16888         
16889         Roo.each(this.cells.elements, function(c){
16890             c.row = 0;
16891             c.events = [];
16892             c.more = [];
16893         });
16894         
16895         Roo.each(this.calevents, function(e) {
16896             Roo.each(e.els, function(el) {
16897                 el.un('mouseenter' ,this.onEventEnter, this);
16898                 el.un('mouseleave' ,this.onEventLeave, this);
16899                 el.remove();
16900             },this);
16901         },this);
16902         
16903         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16904             e.remove();
16905         });
16906         
16907     },
16908     
16909     renderEvents: function()
16910     {   
16911         var _this = this;
16912         
16913         this.cells.each(function(c) {
16914             
16915             if(c.row < 5){
16916                 return;
16917             }
16918             
16919             var ev = c.events;
16920             
16921             var r = 4;
16922             if(c.row != c.events.length){
16923                 r = 4 - (4 - (c.row - c.events.length));
16924             }
16925             
16926             c.events = ev.slice(0, r);
16927             c.more = ev.slice(r);
16928             
16929             if(c.more.length && c.more.length == 1){
16930                 c.events.push(c.more.pop());
16931             }
16932             
16933             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16934             
16935         });
16936             
16937         this.cells.each(function(c) {
16938             
16939             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16940             
16941             
16942             for (var e = 0; e < c.events.length; e++){
16943                 var ev = c.events[e];
16944                 var rows = ev.rows;
16945                 
16946                 for(var i = 0; i < rows.length; i++) {
16947                 
16948                     // how many rows should it span..
16949
16950                     var  cfg = {
16951                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16952                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16953
16954                         unselectable : "on",
16955                         cn : [
16956                             {
16957                                 cls: 'fc-event-inner',
16958                                 cn : [
16959     //                                {
16960     //                                  tag:'span',
16961     //                                  cls: 'fc-event-time',
16962     //                                  html : cells.length > 1 ? '' : ev.time
16963     //                                },
16964                                     {
16965                                       tag:'span',
16966                                       cls: 'fc-event-title',
16967                                       html : String.format('{0}', ev.title)
16968                                     }
16969
16970
16971                                 ]
16972                             },
16973                             {
16974                                 cls: 'ui-resizable-handle ui-resizable-e',
16975                                 html : '&nbsp;&nbsp;&nbsp'
16976                             }
16977
16978                         ]
16979                     };
16980
16981                     if (i == 0) {
16982                         cfg.cls += ' fc-event-start';
16983                     }
16984                     if ((i+1) == rows.length) {
16985                         cfg.cls += ' fc-event-end';
16986                     }
16987
16988                     var ctr = _this.el.select('.fc-event-container',true).first();
16989                     var cg = ctr.createChild(cfg);
16990
16991                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16992                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16993
16994                     var r = (c.more.length) ? 1 : 0;
16995                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16996                     cg.setWidth(ebox.right - sbox.x -2);
16997
16998                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16999                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17000                     cg.on('click', _this.onEventClick, _this, ev);
17001
17002                     ev.els.push(cg);
17003                     
17004                 }
17005                 
17006             }
17007             
17008             
17009             if(c.more.length){
17010                 var  cfg = {
17011                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17012                     style : 'position: absolute',
17013                     unselectable : "on",
17014                     cn : [
17015                         {
17016                             cls: 'fc-event-inner',
17017                             cn : [
17018                                 {
17019                                   tag:'span',
17020                                   cls: 'fc-event-title',
17021                                   html : 'More'
17022                                 }
17023
17024
17025                             ]
17026                         },
17027                         {
17028                             cls: 'ui-resizable-handle ui-resizable-e',
17029                             html : '&nbsp;&nbsp;&nbsp'
17030                         }
17031
17032                     ]
17033                 };
17034
17035                 var ctr = _this.el.select('.fc-event-container',true).first();
17036                 var cg = ctr.createChild(cfg);
17037
17038                 var sbox = c.select('.fc-day-content',true).first().getBox();
17039                 var ebox = c.select('.fc-day-content',true).first().getBox();
17040                 //Roo.log(cg);
17041                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17042                 cg.setWidth(ebox.right - sbox.x -2);
17043
17044                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17045                 
17046             }
17047             
17048         });
17049         
17050         
17051         
17052     },
17053     
17054     onEventEnter: function (e, el,event,d) {
17055         this.fireEvent('evententer', this, el, event);
17056     },
17057     
17058     onEventLeave: function (e, el,event,d) {
17059         this.fireEvent('eventleave', this, el, event);
17060     },
17061     
17062     onEventClick: function (e, el,event,d) {
17063         this.fireEvent('eventclick', this, el, event);
17064     },
17065     
17066     onMonthChange: function () {
17067         this.store.load();
17068     },
17069     
17070     onMoreEventClick: function(e, el, more)
17071     {
17072         var _this = this;
17073         
17074         this.calpopover.placement = 'right';
17075         this.calpopover.setTitle('More');
17076         
17077         this.calpopover.setContent('');
17078         
17079         var ctr = this.calpopover.el.select('.popover-content', true).first();
17080         
17081         Roo.each(more, function(m){
17082             var cfg = {
17083                 cls : 'fc-event-hori fc-event-draggable',
17084                 html : m.title
17085             };
17086             var cg = ctr.createChild(cfg);
17087             
17088             cg.on('click', _this.onEventClick, _this, m);
17089         });
17090         
17091         this.calpopover.show(el);
17092         
17093         
17094     },
17095     
17096     onLoad: function () 
17097     {   
17098         this.calevents = [];
17099         var cal = this;
17100         
17101         if(this.store.getCount() > 0){
17102             this.store.data.each(function(d){
17103                cal.addItem({
17104                     id : d.data.id,
17105                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17106                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17107                     time : d.data.start_time,
17108                     title : d.data.title,
17109                     description : d.data.description,
17110                     venue : d.data.venue
17111                 });
17112             });
17113         }
17114         
17115         this.renderEvents();
17116         
17117         if(this.calevents.length && this.loadMask){
17118             this.maskEl.hide();
17119         }
17120     },
17121     
17122     onBeforeLoad: function()
17123     {
17124         this.clearEvents();
17125         if(this.loadMask){
17126             this.maskEl.show();
17127         }
17128     }
17129 });
17130
17131  
17132  /*
17133  * - LGPL
17134  *
17135  * element
17136  * 
17137  */
17138
17139 /**
17140  * @class Roo.bootstrap.Popover
17141  * @extends Roo.bootstrap.Component
17142  * Bootstrap Popover class
17143  * @cfg {String} html contents of the popover   (or false to use children..)
17144  * @cfg {String} title of popover (or false to hide)
17145  * @cfg {String} placement how it is placed
17146  * @cfg {String} trigger click || hover (or false to trigger manually)
17147  * @cfg {String} over what (parent or false to trigger manually.)
17148  * @cfg {Number} delay - delay before showing
17149  
17150  * @constructor
17151  * Create a new Popover
17152  * @param {Object} config The config object
17153  */
17154
17155 Roo.bootstrap.Popover = function(config){
17156     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17157     
17158     this.addEvents({
17159         // raw events
17160          /**
17161          * @event show
17162          * After the popover show
17163          * 
17164          * @param {Roo.bootstrap.Popover} this
17165          */
17166         "show" : true,
17167         /**
17168          * @event hide
17169          * After the popover hide
17170          * 
17171          * @param {Roo.bootstrap.Popover} this
17172          */
17173         "hide" : true
17174     });
17175 };
17176
17177 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17178     
17179     title: 'Fill in a title',
17180     html: false,
17181     
17182     placement : 'right',
17183     trigger : 'hover', // hover
17184     
17185     delay : 0,
17186     
17187     over: 'parent',
17188     
17189     can_build_overlaid : false,
17190     
17191     getChildContainer : function()
17192     {
17193         return this.el.select('.popover-content',true).first();
17194     },
17195     
17196     getAutoCreate : function(){
17197          
17198         var cfg = {
17199            cls : 'popover roo-dynamic',
17200            style: 'display:block',
17201            cn : [
17202                 {
17203                     cls : 'arrow'
17204                 },
17205                 {
17206                     cls : 'popover-inner',
17207                     cn : [
17208                         {
17209                             tag: 'h3',
17210                             cls: 'popover-title',
17211                             html : this.title
17212                         },
17213                         {
17214                             cls : 'popover-content',
17215                             html : this.html
17216                         }
17217                     ]
17218                     
17219                 }
17220            ]
17221         };
17222         
17223         return cfg;
17224     },
17225     setTitle: function(str)
17226     {
17227         this.title = str;
17228         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17229     },
17230     setContent: function(str)
17231     {
17232         this.html = str;
17233         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17234     },
17235     // as it get's added to the bottom of the page.
17236     onRender : function(ct, position)
17237     {
17238         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17239         if(!this.el){
17240             var cfg = Roo.apply({},  this.getAutoCreate());
17241             cfg.id = Roo.id();
17242             
17243             if (this.cls) {
17244                 cfg.cls += ' ' + this.cls;
17245             }
17246             if (this.style) {
17247                 cfg.style = this.style;
17248             }
17249             //Roo.log("adding to ");
17250             this.el = Roo.get(document.body).createChild(cfg, position);
17251 //            Roo.log(this.el);
17252         }
17253         this.initEvents();
17254     },
17255     
17256     initEvents : function()
17257     {
17258         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17259         this.el.enableDisplayMode('block');
17260         this.el.hide();
17261         if (this.over === false) {
17262             return; 
17263         }
17264         if (this.triggers === false) {
17265             return;
17266         }
17267         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17268         var triggers = this.trigger ? this.trigger.split(' ') : [];
17269         Roo.each(triggers, function(trigger) {
17270         
17271             if (trigger == 'click') {
17272                 on_el.on('click', this.toggle, this);
17273             } else if (trigger != 'manual') {
17274                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17275                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17276       
17277                 on_el.on(eventIn  ,this.enter, this);
17278                 on_el.on(eventOut, this.leave, this);
17279             }
17280         }, this);
17281         
17282     },
17283     
17284     
17285     // private
17286     timeout : null,
17287     hoverState : null,
17288     
17289     toggle : function () {
17290         this.hoverState == 'in' ? this.leave() : this.enter();
17291     },
17292     
17293     enter : function () {
17294         
17295         clearTimeout(this.timeout);
17296     
17297         this.hoverState = 'in';
17298     
17299         if (!this.delay || !this.delay.show) {
17300             this.show();
17301             return;
17302         }
17303         var _t = this;
17304         this.timeout = setTimeout(function () {
17305             if (_t.hoverState == 'in') {
17306                 _t.show();
17307             }
17308         }, this.delay.show)
17309     },
17310     
17311     leave : function() {
17312         clearTimeout(this.timeout);
17313     
17314         this.hoverState = 'out';
17315     
17316         if (!this.delay || !this.delay.hide) {
17317             this.hide();
17318             return;
17319         }
17320         var _t = this;
17321         this.timeout = setTimeout(function () {
17322             if (_t.hoverState == 'out') {
17323                 _t.hide();
17324             }
17325         }, this.delay.hide)
17326     },
17327     
17328     show : function (on_el)
17329     {
17330         if (!on_el) {
17331             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17332         }
17333         
17334         // set content.
17335         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17336         if (this.html !== false) {
17337             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17338         }
17339         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17340         if (!this.title.length) {
17341             this.el.select('.popover-title',true).hide();
17342         }
17343         
17344         var placement = typeof this.placement == 'function' ?
17345             this.placement.call(this, this.el, on_el) :
17346             this.placement;
17347             
17348         var autoToken = /\s?auto?\s?/i;
17349         var autoPlace = autoToken.test(placement);
17350         if (autoPlace) {
17351             placement = placement.replace(autoToken, '') || 'top';
17352         }
17353         
17354         //this.el.detach()
17355         //this.el.setXY([0,0]);
17356         this.el.show();
17357         this.el.dom.style.display='block';
17358         this.el.addClass(placement);
17359         
17360         //this.el.appendTo(on_el);
17361         
17362         var p = this.getPosition();
17363         var box = this.el.getBox();
17364         
17365         if (autoPlace) {
17366             // fixme..
17367         }
17368         var align = Roo.bootstrap.Popover.alignment[placement];
17369         
17370 //        Roo.log(align);
17371         this.el.alignTo(on_el, align[0],align[1]);
17372         //var arrow = this.el.select('.arrow',true).first();
17373         //arrow.set(align[2], 
17374         
17375         this.el.addClass('in');
17376         
17377         
17378         if (this.el.hasClass('fade')) {
17379             // fade it?
17380         }
17381         
17382         this.hoverState = 'in';
17383         
17384         this.fireEvent('show', this);
17385         
17386     },
17387     hide : function()
17388     {
17389         this.el.setXY([0,0]);
17390         this.el.removeClass('in');
17391         this.el.hide();
17392         this.hoverState = null;
17393         
17394         this.fireEvent('hide', this);
17395     }
17396     
17397 });
17398
17399 Roo.bootstrap.Popover.alignment = {
17400     'left' : ['r-l', [-10,0], 'right'],
17401     'right' : ['l-r', [10,0], 'left'],
17402     'bottom' : ['t-b', [0,10], 'top'],
17403     'top' : [ 'b-t', [0,-10], 'bottom']
17404 };
17405
17406  /*
17407  * - LGPL
17408  *
17409  * Progress
17410  * 
17411  */
17412
17413 /**
17414  * @class Roo.bootstrap.Progress
17415  * @extends Roo.bootstrap.Component
17416  * Bootstrap Progress class
17417  * @cfg {Boolean} striped striped of the progress bar
17418  * @cfg {Boolean} active animated of the progress bar
17419  * 
17420  * 
17421  * @constructor
17422  * Create a new Progress
17423  * @param {Object} config The config object
17424  */
17425
17426 Roo.bootstrap.Progress = function(config){
17427     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17428 };
17429
17430 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17431     
17432     striped : false,
17433     active: false,
17434     
17435     getAutoCreate : function(){
17436         var cfg = {
17437             tag: 'div',
17438             cls: 'progress'
17439         };
17440         
17441         
17442         if(this.striped){
17443             cfg.cls += ' progress-striped';
17444         }
17445       
17446         if(this.active){
17447             cfg.cls += ' active';
17448         }
17449         
17450         
17451         return cfg;
17452     }
17453    
17454 });
17455
17456  
17457
17458  /*
17459  * - LGPL
17460  *
17461  * ProgressBar
17462  * 
17463  */
17464
17465 /**
17466  * @class Roo.bootstrap.ProgressBar
17467  * @extends Roo.bootstrap.Component
17468  * Bootstrap ProgressBar class
17469  * @cfg {Number} aria_valuenow aria-value now
17470  * @cfg {Number} aria_valuemin aria-value min
17471  * @cfg {Number} aria_valuemax aria-value max
17472  * @cfg {String} label label for the progress bar
17473  * @cfg {String} panel (success | info | warning | danger )
17474  * @cfg {String} role role of the progress bar
17475  * @cfg {String} sr_only text
17476  * 
17477  * 
17478  * @constructor
17479  * Create a new ProgressBar
17480  * @param {Object} config The config object
17481  */
17482
17483 Roo.bootstrap.ProgressBar = function(config){
17484     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17485 };
17486
17487 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17488     
17489     aria_valuenow : 0,
17490     aria_valuemin : 0,
17491     aria_valuemax : 100,
17492     label : false,
17493     panel : false,
17494     role : false,
17495     sr_only: false,
17496     
17497     getAutoCreate : function()
17498     {
17499         
17500         var cfg = {
17501             tag: 'div',
17502             cls: 'progress-bar',
17503             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17504         };
17505         
17506         if(this.sr_only){
17507             cfg.cn = {
17508                 tag: 'span',
17509                 cls: 'sr-only',
17510                 html: this.sr_only
17511             }
17512         }
17513         
17514         if(this.role){
17515             cfg.role = this.role;
17516         }
17517         
17518         if(this.aria_valuenow){
17519             cfg['aria-valuenow'] = this.aria_valuenow;
17520         }
17521         
17522         if(this.aria_valuemin){
17523             cfg['aria-valuemin'] = this.aria_valuemin;
17524         }
17525         
17526         if(this.aria_valuemax){
17527             cfg['aria-valuemax'] = this.aria_valuemax;
17528         }
17529         
17530         if(this.label && !this.sr_only){
17531             cfg.html = this.label;
17532         }
17533         
17534         if(this.panel){
17535             cfg.cls += ' progress-bar-' + this.panel;
17536         }
17537         
17538         return cfg;
17539     },
17540     
17541     update : function(aria_valuenow)
17542     {
17543         this.aria_valuenow = aria_valuenow;
17544         
17545         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17546     }
17547    
17548 });
17549
17550  
17551
17552  /*
17553  * - LGPL
17554  *
17555  * column
17556  * 
17557  */
17558
17559 /**
17560  * @class Roo.bootstrap.TabGroup
17561  * @extends Roo.bootstrap.Column
17562  * Bootstrap Column class
17563  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17564  * @cfg {Boolean} carousel true to make the group behave like a carousel
17565  * @cfg {Boolean} bullets show bullets for the panels
17566  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17567  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17568  * @cfg {Boolean} showarrow (true|false) show arrow default true
17569  * 
17570  * @constructor
17571  * Create a new TabGroup
17572  * @param {Object} config The config object
17573  */
17574
17575 Roo.bootstrap.TabGroup = function(config){
17576     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17577     if (!this.navId) {
17578         this.navId = Roo.id();
17579     }
17580     this.tabs = [];
17581     Roo.bootstrap.TabGroup.register(this);
17582     
17583 };
17584
17585 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17586     
17587     carousel : false,
17588     transition : false,
17589     bullets : 0,
17590     timer : 0,
17591     autoslide : false,
17592     slideFn : false,
17593     slideOnTouch : false,
17594     showarrow : true,
17595     
17596     getAutoCreate : function()
17597     {
17598         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17599         
17600         cfg.cls += ' tab-content';
17601         
17602         if (this.carousel) {
17603             cfg.cls += ' carousel slide';
17604             
17605             cfg.cn = [{
17606                cls : 'carousel-inner',
17607                cn : []
17608             }];
17609         
17610             if(this.bullets  && !Roo.isTouch){
17611                 
17612                 var bullets = {
17613                     cls : 'carousel-bullets',
17614                     cn : []
17615                 };
17616                
17617                 if(this.bullets_cls){
17618                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17619                 }
17620                 
17621                 bullets.cn.push({
17622                     cls : 'clear'
17623                 });
17624                 
17625                 cfg.cn[0].cn.push(bullets);
17626             }
17627             
17628             if(this.showarrow){
17629                 cfg.cn[0].cn.push({
17630                     tag : 'div',
17631                     class : 'carousel-arrow',
17632                     cn : [
17633                         {
17634                             tag : 'div',
17635                             class : 'carousel-prev',
17636                             cn : [
17637                                 {
17638                                     tag : 'i',
17639                                     class : 'fa fa-chevron-left'
17640                                 }
17641                             ]
17642                         },
17643                         {
17644                             tag : 'div',
17645                             class : 'carousel-next',
17646                             cn : [
17647                                 {
17648                                     tag : 'i',
17649                                     class : 'fa fa-chevron-right'
17650                                 }
17651                             ]
17652                         }
17653                     ]
17654                 });
17655             }
17656             
17657         }
17658         
17659         return cfg;
17660     },
17661     
17662     initEvents:  function()
17663     {
17664 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17665 //            this.el.on("touchstart", this.onTouchStart, this);
17666 //        }
17667         
17668         if(this.autoslide){
17669             var _this = this;
17670             
17671             this.slideFn = window.setInterval(function() {
17672                 _this.showPanelNext();
17673             }, this.timer);
17674         }
17675         
17676         if(this.showarrow){
17677             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17678             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17679         }
17680         
17681         
17682     },
17683     
17684 //    onTouchStart : function(e, el, o)
17685 //    {
17686 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17687 //            return;
17688 //        }
17689 //        
17690 //        this.showPanelNext();
17691 //    },
17692     
17693     
17694     getChildContainer : function()
17695     {
17696         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17697     },
17698     
17699     /**
17700     * register a Navigation item
17701     * @param {Roo.bootstrap.NavItem} the navitem to add
17702     */
17703     register : function(item)
17704     {
17705         this.tabs.push( item);
17706         item.navId = this.navId; // not really needed..
17707         this.addBullet();
17708     
17709     },
17710     
17711     getActivePanel : function()
17712     {
17713         var r = false;
17714         Roo.each(this.tabs, function(t) {
17715             if (t.active) {
17716                 r = t;
17717                 return false;
17718             }
17719             return null;
17720         });
17721         return r;
17722         
17723     },
17724     getPanelByName : function(n)
17725     {
17726         var r = false;
17727         Roo.each(this.tabs, function(t) {
17728             if (t.tabId == n) {
17729                 r = t;
17730                 return false;
17731             }
17732             return null;
17733         });
17734         return r;
17735     },
17736     indexOfPanel : function(p)
17737     {
17738         var r = false;
17739         Roo.each(this.tabs, function(t,i) {
17740             if (t.tabId == p.tabId) {
17741                 r = i;
17742                 return false;
17743             }
17744             return null;
17745         });
17746         return r;
17747     },
17748     /**
17749      * show a specific panel
17750      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17751      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17752      */
17753     showPanel : function (pan)
17754     {
17755         if(this.transition || typeof(pan) == 'undefined'){
17756             Roo.log("waiting for the transitionend");
17757             return;
17758         }
17759         
17760         if (typeof(pan) == 'number') {
17761             pan = this.tabs[pan];
17762         }
17763         
17764         if (typeof(pan) == 'string') {
17765             pan = this.getPanelByName(pan);
17766         }
17767         
17768         var cur = this.getActivePanel();
17769         
17770         if(!pan || !cur){
17771             Roo.log('pan or acitve pan is undefined');
17772             return false;
17773         }
17774         
17775         if (pan.tabId == this.getActivePanel().tabId) {
17776             return true;
17777         }
17778         
17779         if (false === cur.fireEvent('beforedeactivate')) {
17780             return false;
17781         }
17782         
17783         if(this.bullets > 0 && !Roo.isTouch){
17784             this.setActiveBullet(this.indexOfPanel(pan));
17785         }
17786         
17787         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17788             
17789             this.transition = true;
17790             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17791             var lr = dir == 'next' ? 'left' : 'right';
17792             pan.el.addClass(dir); // or prev
17793             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17794             cur.el.addClass(lr); // or right
17795             pan.el.addClass(lr);
17796             
17797             var _this = this;
17798             cur.el.on('transitionend', function() {
17799                 Roo.log("trans end?");
17800                 
17801                 pan.el.removeClass([lr,dir]);
17802                 pan.setActive(true);
17803                 
17804                 cur.el.removeClass([lr]);
17805                 cur.setActive(false);
17806                 
17807                 _this.transition = false;
17808                 
17809             }, this, { single:  true } );
17810             
17811             return true;
17812         }
17813         
17814         cur.setActive(false);
17815         pan.setActive(true);
17816         
17817         return true;
17818         
17819     },
17820     showPanelNext : function()
17821     {
17822         var i = this.indexOfPanel(this.getActivePanel());
17823         
17824         if (i >= this.tabs.length - 1 && !this.autoslide) {
17825             return;
17826         }
17827         
17828         if (i >= this.tabs.length - 1 && this.autoslide) {
17829             i = -1;
17830         }
17831         
17832         this.showPanel(this.tabs[i+1]);
17833     },
17834     
17835     showPanelPrev : function()
17836     {
17837         var i = this.indexOfPanel(this.getActivePanel());
17838         
17839         if (i  < 1 && !this.autoslide) {
17840             return;
17841         }
17842         
17843         if (i < 1 && this.autoslide) {
17844             i = this.tabs.length;
17845         }
17846         
17847         this.showPanel(this.tabs[i-1]);
17848     },
17849     
17850     
17851     addBullet: function()
17852     {
17853         if(!this.bullets || Roo.isTouch){
17854             return;
17855         }
17856         var ctr = this.el.select('.carousel-bullets',true).first();
17857         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17858         var bullet = ctr.createChild({
17859             cls : 'bullet bullet-' + i
17860         },ctr.dom.lastChild);
17861         
17862         
17863         var _this = this;
17864         
17865         bullet.on('click', (function(e, el, o, ii, t){
17866
17867             e.preventDefault();
17868
17869             this.showPanel(ii);
17870
17871             if(this.autoslide && this.slideFn){
17872                 clearInterval(this.slideFn);
17873                 this.slideFn = window.setInterval(function() {
17874                     _this.showPanelNext();
17875                 }, this.timer);
17876             }
17877
17878         }).createDelegate(this, [i, bullet], true));
17879                 
17880         
17881     },
17882      
17883     setActiveBullet : function(i)
17884     {
17885         if(Roo.isTouch){
17886             return;
17887         }
17888         
17889         Roo.each(this.el.select('.bullet', true).elements, function(el){
17890             el.removeClass('selected');
17891         });
17892
17893         var bullet = this.el.select('.bullet-' + i, true).first();
17894         
17895         if(!bullet){
17896             return;
17897         }
17898         
17899         bullet.addClass('selected');
17900     }
17901     
17902     
17903   
17904 });
17905
17906  
17907
17908  
17909  
17910 Roo.apply(Roo.bootstrap.TabGroup, {
17911     
17912     groups: {},
17913      /**
17914     * register a Navigation Group
17915     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17916     */
17917     register : function(navgrp)
17918     {
17919         this.groups[navgrp.navId] = navgrp;
17920         
17921     },
17922     /**
17923     * fetch a Navigation Group based on the navigation ID
17924     * if one does not exist , it will get created.
17925     * @param {string} the navgroup to add
17926     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17927     */
17928     get: function(navId) {
17929         if (typeof(this.groups[navId]) == 'undefined') {
17930             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17931         }
17932         return this.groups[navId] ;
17933     }
17934     
17935     
17936     
17937 });
17938
17939  /*
17940  * - LGPL
17941  *
17942  * TabPanel
17943  * 
17944  */
17945
17946 /**
17947  * @class Roo.bootstrap.TabPanel
17948  * @extends Roo.bootstrap.Component
17949  * Bootstrap TabPanel class
17950  * @cfg {Boolean} active panel active
17951  * @cfg {String} html panel content
17952  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17953  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17954  * @cfg {String} href click to link..
17955  * 
17956  * 
17957  * @constructor
17958  * Create a new TabPanel
17959  * @param {Object} config The config object
17960  */
17961
17962 Roo.bootstrap.TabPanel = function(config){
17963     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17964     this.addEvents({
17965         /**
17966              * @event changed
17967              * Fires when the active status changes
17968              * @param {Roo.bootstrap.TabPanel} this
17969              * @param {Boolean} state the new state
17970             
17971          */
17972         'changed': true,
17973         /**
17974              * @event beforedeactivate
17975              * Fires before a tab is de-activated - can be used to do validation on a form.
17976              * @param {Roo.bootstrap.TabPanel} this
17977              * @return {Boolean} false if there is an error
17978             
17979          */
17980         'beforedeactivate': true
17981      });
17982     
17983     this.tabId = this.tabId || Roo.id();
17984   
17985 };
17986
17987 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17988     
17989     active: false,
17990     html: false,
17991     tabId: false,
17992     navId : false,
17993     href : '',
17994     
17995     getAutoCreate : function(){
17996         var cfg = {
17997             tag: 'div',
17998             // item is needed for carousel - not sure if it has any effect otherwise
17999             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18000             html: this.html || ''
18001         };
18002         
18003         if(this.active){
18004             cfg.cls += ' active';
18005         }
18006         
18007         if(this.tabId){
18008             cfg.tabId = this.tabId;
18009         }
18010         
18011         
18012         return cfg;
18013     },
18014     
18015     initEvents:  function()
18016     {
18017         var p = this.parent();
18018         
18019         this.navId = this.navId || p.navId;
18020         
18021         if (typeof(this.navId) != 'undefined') {
18022             // not really needed.. but just in case.. parent should be a NavGroup.
18023             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18024             
18025             tg.register(this);
18026             
18027             var i = tg.tabs.length - 1;
18028             
18029             if(this.active && tg.bullets > 0 && i < tg.bullets){
18030                 tg.setActiveBullet(i);
18031             }
18032         }
18033         
18034         this.el.on('click', this.onClick, this);
18035         
18036         if(Roo.isTouch){
18037             this.el.on("touchstart", this.onTouchStart, this);
18038             this.el.on("touchmove", this.onTouchMove, this);
18039             this.el.on("touchend", this.onTouchEnd, this);
18040         }
18041         
18042     },
18043     
18044     onRender : function(ct, position)
18045     {
18046         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18047     },
18048     
18049     setActive : function(state)
18050     {
18051         Roo.log("panel - set active " + this.tabId + "=" + state);
18052         
18053         this.active = state;
18054         if (!state) {
18055             this.el.removeClass('active');
18056             
18057         } else  if (!this.el.hasClass('active')) {
18058             this.el.addClass('active');
18059         }
18060         
18061         this.fireEvent('changed', this, state);
18062     },
18063     
18064     onClick : function(e)
18065     {
18066         e.preventDefault();
18067         
18068         if(!this.href.length){
18069             return;
18070         }
18071         
18072         window.location.href = this.href;
18073     },
18074     
18075     startX : 0,
18076     startY : 0,
18077     endX : 0,
18078     endY : 0,
18079     swiping : false,
18080     
18081     onTouchStart : function(e)
18082     {
18083         this.swiping = false;
18084         
18085         this.startX = e.browserEvent.touches[0].clientX;
18086         this.startY = e.browserEvent.touches[0].clientY;
18087     },
18088     
18089     onTouchMove : function(e)
18090     {
18091         this.swiping = true;
18092         
18093         this.endX = e.browserEvent.touches[0].clientX;
18094         this.endY = e.browserEvent.touches[0].clientY;
18095     },
18096     
18097     onTouchEnd : function(e)
18098     {
18099         if(!this.swiping){
18100             this.onClick(e);
18101             return;
18102         }
18103         
18104         var tabGroup = this.parent();
18105         
18106         if(this.endX > this.startX){ // swiping right
18107             tabGroup.showPanelPrev();
18108             return;
18109         }
18110         
18111         if(this.startX > this.endX){ // swiping left
18112             tabGroup.showPanelNext();
18113             return;
18114         }
18115     }
18116     
18117     
18118 });
18119  
18120
18121  
18122
18123  /*
18124  * - LGPL
18125  *
18126  * DateField
18127  * 
18128  */
18129
18130 /**
18131  * @class Roo.bootstrap.DateField
18132  * @extends Roo.bootstrap.Input
18133  * Bootstrap DateField class
18134  * @cfg {Number} weekStart default 0
18135  * @cfg {String} viewMode default empty, (months|years)
18136  * @cfg {String} minViewMode default empty, (months|years)
18137  * @cfg {Number} startDate default -Infinity
18138  * @cfg {Number} endDate default Infinity
18139  * @cfg {Boolean} todayHighlight default false
18140  * @cfg {Boolean} todayBtn default false
18141  * @cfg {Boolean} calendarWeeks default false
18142  * @cfg {Object} daysOfWeekDisabled default empty
18143  * @cfg {Boolean} singleMode default false (true | false)
18144  * 
18145  * @cfg {Boolean} keyboardNavigation default true
18146  * @cfg {String} language default en
18147  * 
18148  * @constructor
18149  * Create a new DateField
18150  * @param {Object} config The config object
18151  */
18152
18153 Roo.bootstrap.DateField = function(config){
18154     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18155      this.addEvents({
18156             /**
18157              * @event show
18158              * Fires when this field show.
18159              * @param {Roo.bootstrap.DateField} this
18160              * @param {Mixed} date The date value
18161              */
18162             show : true,
18163             /**
18164              * @event show
18165              * Fires when this field hide.
18166              * @param {Roo.bootstrap.DateField} this
18167              * @param {Mixed} date The date value
18168              */
18169             hide : true,
18170             /**
18171              * @event select
18172              * Fires when select a date.
18173              * @param {Roo.bootstrap.DateField} this
18174              * @param {Mixed} date The date value
18175              */
18176             select : true,
18177             /**
18178              * @event beforeselect
18179              * Fires when before select a date.
18180              * @param {Roo.bootstrap.DateField} this
18181              * @param {Mixed} date The date value
18182              */
18183             beforeselect : true
18184         });
18185 };
18186
18187 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18188     
18189     /**
18190      * @cfg {String} format
18191      * The default date format string which can be overriden for localization support.  The format must be
18192      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18193      */
18194     format : "m/d/y",
18195     /**
18196      * @cfg {String} altFormats
18197      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18198      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18199      */
18200     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18201     
18202     weekStart : 0,
18203     
18204     viewMode : '',
18205     
18206     minViewMode : '',
18207     
18208     todayHighlight : false,
18209     
18210     todayBtn: false,
18211     
18212     language: 'en',
18213     
18214     keyboardNavigation: true,
18215     
18216     calendarWeeks: false,
18217     
18218     startDate: -Infinity,
18219     
18220     endDate: Infinity,
18221     
18222     daysOfWeekDisabled: [],
18223     
18224     _events: [],
18225     
18226     singleMode : false,
18227     
18228     UTCDate: function()
18229     {
18230         return new Date(Date.UTC.apply(Date, arguments));
18231     },
18232     
18233     UTCToday: function()
18234     {
18235         var today = new Date();
18236         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18237     },
18238     
18239     getDate: function() {
18240             var d = this.getUTCDate();
18241             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18242     },
18243     
18244     getUTCDate: function() {
18245             return this.date;
18246     },
18247     
18248     setDate: function(d) {
18249             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18250     },
18251     
18252     setUTCDate: function(d) {
18253             this.date = d;
18254             this.setValue(this.formatDate(this.date));
18255     },
18256         
18257     onRender: function(ct, position)
18258     {
18259         
18260         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18261         
18262         this.language = this.language || 'en';
18263         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18264         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18265         
18266         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18267         this.format = this.format || 'm/d/y';
18268         this.isInline = false;
18269         this.isInput = true;
18270         this.component = this.el.select('.add-on', true).first() || false;
18271         this.component = (this.component && this.component.length === 0) ? false : this.component;
18272         this.hasInput = this.component && this.inputEl().length;
18273         
18274         if (typeof(this.minViewMode === 'string')) {
18275             switch (this.minViewMode) {
18276                 case 'months':
18277                     this.minViewMode = 1;
18278                     break;
18279                 case 'years':
18280                     this.minViewMode = 2;
18281                     break;
18282                 default:
18283                     this.minViewMode = 0;
18284                     break;
18285             }
18286         }
18287         
18288         if (typeof(this.viewMode === 'string')) {
18289             switch (this.viewMode) {
18290                 case 'months':
18291                     this.viewMode = 1;
18292                     break;
18293                 case 'years':
18294                     this.viewMode = 2;
18295                     break;
18296                 default:
18297                     this.viewMode = 0;
18298                     break;
18299             }
18300         }
18301                 
18302         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18303         
18304 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18305         
18306         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18307         
18308         this.picker().on('mousedown', this.onMousedown, this);
18309         this.picker().on('click', this.onClick, this);
18310         
18311         this.picker().addClass('datepicker-dropdown');
18312         
18313         this.startViewMode = this.viewMode;
18314         
18315         if(this.singleMode){
18316             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18317                 v.setVisibilityMode(Roo.Element.DISPLAY);
18318                 v.hide();
18319             });
18320             
18321             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18322                 v.setStyle('width', '189px');
18323             });
18324         }
18325         
18326         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18327             if(!this.calendarWeeks){
18328                 v.remove();
18329                 return;
18330             }
18331             
18332             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18333             v.attr('colspan', function(i, val){
18334                 return parseInt(val) + 1;
18335             });
18336         });
18337                         
18338         
18339         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18340         
18341         this.setStartDate(this.startDate);
18342         this.setEndDate(this.endDate);
18343         
18344         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18345         
18346         this.fillDow();
18347         this.fillMonths();
18348         this.update();
18349         this.showMode();
18350         
18351         if(this.isInline) {
18352             this.show();
18353         }
18354     },
18355     
18356     picker : function()
18357     {
18358         return this.pickerEl;
18359 //        return this.el.select('.datepicker', true).first();
18360     },
18361     
18362     fillDow: function()
18363     {
18364         var dowCnt = this.weekStart;
18365         
18366         var dow = {
18367             tag: 'tr',
18368             cn: [
18369                 
18370             ]
18371         };
18372         
18373         if(this.calendarWeeks){
18374             dow.cn.push({
18375                 tag: 'th',
18376                 cls: 'cw',
18377                 html: '&nbsp;'
18378             })
18379         }
18380         
18381         while (dowCnt < this.weekStart + 7) {
18382             dow.cn.push({
18383                 tag: 'th',
18384                 cls: 'dow',
18385                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18386             });
18387         }
18388         
18389         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18390     },
18391     
18392     fillMonths: function()
18393     {    
18394         var i = 0;
18395         var months = this.picker().select('>.datepicker-months td', true).first();
18396         
18397         months.dom.innerHTML = '';
18398         
18399         while (i < 12) {
18400             var month = {
18401                 tag: 'span',
18402                 cls: 'month',
18403                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18404             };
18405             
18406             months.createChild(month);
18407         }
18408         
18409     },
18410     
18411     update: function()
18412     {
18413         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;
18414         
18415         if (this.date < this.startDate) {
18416             this.viewDate = new Date(this.startDate);
18417         } else if (this.date > this.endDate) {
18418             this.viewDate = new Date(this.endDate);
18419         } else {
18420             this.viewDate = new Date(this.date);
18421         }
18422         
18423         this.fill();
18424     },
18425     
18426     fill: function() 
18427     {
18428         var d = new Date(this.viewDate),
18429                 year = d.getUTCFullYear(),
18430                 month = d.getUTCMonth(),
18431                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18432                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18433                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18434                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18435                 currentDate = this.date && this.date.valueOf(),
18436                 today = this.UTCToday();
18437         
18438         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18439         
18440 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18441         
18442 //        this.picker.select('>tfoot th.today').
18443 //                                              .text(dates[this.language].today)
18444 //                                              .toggle(this.todayBtn !== false);
18445     
18446         this.updateNavArrows();
18447         this.fillMonths();
18448                                                 
18449         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18450         
18451         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18452          
18453         prevMonth.setUTCDate(day);
18454         
18455         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18456         
18457         var nextMonth = new Date(prevMonth);
18458         
18459         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18460         
18461         nextMonth = nextMonth.valueOf();
18462         
18463         var fillMonths = false;
18464         
18465         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18466         
18467         while(prevMonth.valueOf() < nextMonth) {
18468             var clsName = '';
18469             
18470             if (prevMonth.getUTCDay() === this.weekStart) {
18471                 if(fillMonths){
18472                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18473                 }
18474                     
18475                 fillMonths = {
18476                     tag: 'tr',
18477                     cn: []
18478                 };
18479                 
18480                 if(this.calendarWeeks){
18481                     // ISO 8601: First week contains first thursday.
18482                     // ISO also states week starts on Monday, but we can be more abstract here.
18483                     var
18484                     // Start of current week: based on weekstart/current date
18485                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18486                     // Thursday of this week
18487                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18488                     // First Thursday of year, year from thursday
18489                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18490                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18491                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18492                     
18493                     fillMonths.cn.push({
18494                         tag: 'td',
18495                         cls: 'cw',
18496                         html: calWeek
18497                     });
18498                 }
18499             }
18500             
18501             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18502                 clsName += ' old';
18503             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18504                 clsName += ' new';
18505             }
18506             if (this.todayHighlight &&
18507                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18508                 prevMonth.getUTCMonth() == today.getMonth() &&
18509                 prevMonth.getUTCDate() == today.getDate()) {
18510                 clsName += ' today';
18511             }
18512             
18513             if (currentDate && prevMonth.valueOf() === currentDate) {
18514                 clsName += ' active';
18515             }
18516             
18517             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18518                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18519                     clsName += ' disabled';
18520             }
18521             
18522             fillMonths.cn.push({
18523                 tag: 'td',
18524                 cls: 'day ' + clsName,
18525                 html: prevMonth.getDate()
18526             });
18527             
18528             prevMonth.setDate(prevMonth.getDate()+1);
18529         }
18530           
18531         var currentYear = this.date && this.date.getUTCFullYear();
18532         var currentMonth = this.date && this.date.getUTCMonth();
18533         
18534         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18535         
18536         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18537             v.removeClass('active');
18538             
18539             if(currentYear === year && k === currentMonth){
18540                 v.addClass('active');
18541             }
18542             
18543             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18544                 v.addClass('disabled');
18545             }
18546             
18547         });
18548         
18549         
18550         year = parseInt(year/10, 10) * 10;
18551         
18552         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18553         
18554         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18555         
18556         year -= 1;
18557         for (var i = -1; i < 11; i++) {
18558             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18559                 tag: 'span',
18560                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18561                 html: year
18562             });
18563             
18564             year += 1;
18565         }
18566     },
18567     
18568     showMode: function(dir) 
18569     {
18570         if (dir) {
18571             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18572         }
18573         
18574         Roo.each(this.picker().select('>div',true).elements, function(v){
18575             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18576             v.hide();
18577         });
18578         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18579     },
18580     
18581     place: function()
18582     {
18583         if(this.isInline) {
18584             return;
18585         }
18586         
18587         this.picker().removeClass(['bottom', 'top']);
18588         
18589         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18590             /*
18591              * place to the top of element!
18592              *
18593              */
18594             
18595             this.picker().addClass('top');
18596             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18597             
18598             return;
18599         }
18600         
18601         this.picker().addClass('bottom');
18602         
18603         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18604     },
18605     
18606     parseDate : function(value)
18607     {
18608         if(!value || value instanceof Date){
18609             return value;
18610         }
18611         var v = Date.parseDate(value, this.format);
18612         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18613             v = Date.parseDate(value, 'Y-m-d');
18614         }
18615         if(!v && this.altFormats){
18616             if(!this.altFormatsArray){
18617                 this.altFormatsArray = this.altFormats.split("|");
18618             }
18619             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18620                 v = Date.parseDate(value, this.altFormatsArray[i]);
18621             }
18622         }
18623         return v;
18624     },
18625     
18626     formatDate : function(date, fmt)
18627     {   
18628         return (!date || !(date instanceof Date)) ?
18629         date : date.dateFormat(fmt || this.format);
18630     },
18631     
18632     onFocus : function()
18633     {
18634         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18635         this.show();
18636     },
18637     
18638     onBlur : function()
18639     {
18640         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18641         
18642         var d = this.inputEl().getValue();
18643         
18644         this.setValue(d);
18645                 
18646         this.hide();
18647     },
18648     
18649     show : function()
18650     {
18651         this.picker().show();
18652         this.update();
18653         this.place();
18654         
18655         this.fireEvent('show', this, this.date);
18656     },
18657     
18658     hide : function()
18659     {
18660         if(this.isInline) {
18661             return;
18662         }
18663         this.picker().hide();
18664         this.viewMode = this.startViewMode;
18665         this.showMode();
18666         
18667         this.fireEvent('hide', this, this.date);
18668         
18669     },
18670     
18671     onMousedown: function(e)
18672     {
18673         e.stopPropagation();
18674         e.preventDefault();
18675     },
18676     
18677     keyup: function(e)
18678     {
18679         Roo.bootstrap.DateField.superclass.keyup.call(this);
18680         this.update();
18681     },
18682
18683     setValue: function(v)
18684     {
18685         if(this.fireEvent('beforeselect', this, v) !== false){
18686             var d = new Date(this.parseDate(v) ).clearTime();
18687         
18688             if(isNaN(d.getTime())){
18689                 this.date = this.viewDate = '';
18690                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18691                 return;
18692             }
18693
18694             v = this.formatDate(d);
18695
18696             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18697
18698             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18699
18700             this.update();
18701
18702             this.fireEvent('select', this, this.date);
18703         }
18704     },
18705     
18706     getValue: function()
18707     {
18708         return this.formatDate(this.date);
18709     },
18710     
18711     fireKey: function(e)
18712     {
18713         if (!this.picker().isVisible()){
18714             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18715                 this.show();
18716             }
18717             return;
18718         }
18719         
18720         var dateChanged = false,
18721         dir, day, month,
18722         newDate, newViewDate;
18723         
18724         switch(e.keyCode){
18725             case 27: // escape
18726                 this.hide();
18727                 e.preventDefault();
18728                 break;
18729             case 37: // left
18730             case 39: // right
18731                 if (!this.keyboardNavigation) {
18732                     break;
18733                 }
18734                 dir = e.keyCode == 37 ? -1 : 1;
18735                 
18736                 if (e.ctrlKey){
18737                     newDate = this.moveYear(this.date, dir);
18738                     newViewDate = this.moveYear(this.viewDate, dir);
18739                 } else if (e.shiftKey){
18740                     newDate = this.moveMonth(this.date, dir);
18741                     newViewDate = this.moveMonth(this.viewDate, dir);
18742                 } else {
18743                     newDate = new Date(this.date);
18744                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18745                     newViewDate = new Date(this.viewDate);
18746                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18747                 }
18748                 if (this.dateWithinRange(newDate)){
18749                     this.date = newDate;
18750                     this.viewDate = newViewDate;
18751                     this.setValue(this.formatDate(this.date));
18752 //                    this.update();
18753                     e.preventDefault();
18754                     dateChanged = true;
18755                 }
18756                 break;
18757             case 38: // up
18758             case 40: // down
18759                 if (!this.keyboardNavigation) {
18760                     break;
18761                 }
18762                 dir = e.keyCode == 38 ? -1 : 1;
18763                 if (e.ctrlKey){
18764                     newDate = this.moveYear(this.date, dir);
18765                     newViewDate = this.moveYear(this.viewDate, dir);
18766                 } else if (e.shiftKey){
18767                     newDate = this.moveMonth(this.date, dir);
18768                     newViewDate = this.moveMonth(this.viewDate, dir);
18769                 } else {
18770                     newDate = new Date(this.date);
18771                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18772                     newViewDate = new Date(this.viewDate);
18773                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18774                 }
18775                 if (this.dateWithinRange(newDate)){
18776                     this.date = newDate;
18777                     this.viewDate = newViewDate;
18778                     this.setValue(this.formatDate(this.date));
18779 //                    this.update();
18780                     e.preventDefault();
18781                     dateChanged = true;
18782                 }
18783                 break;
18784             case 13: // enter
18785                 this.setValue(this.formatDate(this.date));
18786                 this.hide();
18787                 e.preventDefault();
18788                 break;
18789             case 9: // tab
18790                 this.setValue(this.formatDate(this.date));
18791                 this.hide();
18792                 break;
18793             case 16: // shift
18794             case 17: // ctrl
18795             case 18: // alt
18796                 break;
18797             default :
18798                 this.hide();
18799                 
18800         }
18801     },
18802     
18803     
18804     onClick: function(e) 
18805     {
18806         e.stopPropagation();
18807         e.preventDefault();
18808         
18809         var target = e.getTarget();
18810         
18811         if(target.nodeName.toLowerCase() === 'i'){
18812             target = Roo.get(target).dom.parentNode;
18813         }
18814         
18815         var nodeName = target.nodeName;
18816         var className = target.className;
18817         var html = target.innerHTML;
18818         //Roo.log(nodeName);
18819         
18820         switch(nodeName.toLowerCase()) {
18821             case 'th':
18822                 switch(className) {
18823                     case 'switch':
18824                         this.showMode(1);
18825                         break;
18826                     case 'prev':
18827                     case 'next':
18828                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18829                         switch(this.viewMode){
18830                                 case 0:
18831                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18832                                         break;
18833                                 case 1:
18834                                 case 2:
18835                                         this.viewDate = this.moveYear(this.viewDate, dir);
18836                                         break;
18837                         }
18838                         this.fill();
18839                         break;
18840                     case 'today':
18841                         var date = new Date();
18842                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18843 //                        this.fill()
18844                         this.setValue(this.formatDate(this.date));
18845                         
18846                         this.hide();
18847                         break;
18848                 }
18849                 break;
18850             case 'span':
18851                 if (className.indexOf('disabled') < 0) {
18852                     this.viewDate.setUTCDate(1);
18853                     if (className.indexOf('month') > -1) {
18854                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18855                     } else {
18856                         var year = parseInt(html, 10) || 0;
18857                         this.viewDate.setUTCFullYear(year);
18858                         
18859                     }
18860                     
18861                     if(this.singleMode){
18862                         this.setValue(this.formatDate(this.viewDate));
18863                         this.hide();
18864                         return;
18865                     }
18866                     
18867                     this.showMode(-1);
18868                     this.fill();
18869                 }
18870                 break;
18871                 
18872             case 'td':
18873                 //Roo.log(className);
18874                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18875                     var day = parseInt(html, 10) || 1;
18876                     var year = this.viewDate.getUTCFullYear(),
18877                         month = this.viewDate.getUTCMonth();
18878
18879                     if (className.indexOf('old') > -1) {
18880                         if(month === 0 ){
18881                             month = 11;
18882                             year -= 1;
18883                         }else{
18884                             month -= 1;
18885                         }
18886                     } else if (className.indexOf('new') > -1) {
18887                         if (month == 11) {
18888                             month = 0;
18889                             year += 1;
18890                         } else {
18891                             month += 1;
18892                         }
18893                     }
18894                     //Roo.log([year,month,day]);
18895                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18896                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18897 //                    this.fill();
18898                     //Roo.log(this.formatDate(this.date));
18899                     this.setValue(this.formatDate(this.date));
18900                     this.hide();
18901                 }
18902                 break;
18903         }
18904     },
18905     
18906     setStartDate: function(startDate)
18907     {
18908         this.startDate = startDate || -Infinity;
18909         if (this.startDate !== -Infinity) {
18910             this.startDate = this.parseDate(this.startDate);
18911         }
18912         this.update();
18913         this.updateNavArrows();
18914     },
18915
18916     setEndDate: function(endDate)
18917     {
18918         this.endDate = endDate || Infinity;
18919         if (this.endDate !== Infinity) {
18920             this.endDate = this.parseDate(this.endDate);
18921         }
18922         this.update();
18923         this.updateNavArrows();
18924     },
18925     
18926     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18927     {
18928         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18929         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18930             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18931         }
18932         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18933             return parseInt(d, 10);
18934         });
18935         this.update();
18936         this.updateNavArrows();
18937     },
18938     
18939     updateNavArrows: function() 
18940     {
18941         if(this.singleMode){
18942             return;
18943         }
18944         
18945         var d = new Date(this.viewDate),
18946         year = d.getUTCFullYear(),
18947         month = d.getUTCMonth();
18948         
18949         Roo.each(this.picker().select('.prev', true).elements, function(v){
18950             v.show();
18951             switch (this.viewMode) {
18952                 case 0:
18953
18954                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18955                         v.hide();
18956                     }
18957                     break;
18958                 case 1:
18959                 case 2:
18960                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18961                         v.hide();
18962                     }
18963                     break;
18964             }
18965         });
18966         
18967         Roo.each(this.picker().select('.next', true).elements, function(v){
18968             v.show();
18969             switch (this.viewMode) {
18970                 case 0:
18971
18972                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18973                         v.hide();
18974                     }
18975                     break;
18976                 case 1:
18977                 case 2:
18978                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18979                         v.hide();
18980                     }
18981                     break;
18982             }
18983         })
18984     },
18985     
18986     moveMonth: function(date, dir)
18987     {
18988         if (!dir) {
18989             return date;
18990         }
18991         var new_date = new Date(date.valueOf()),
18992         day = new_date.getUTCDate(),
18993         month = new_date.getUTCMonth(),
18994         mag = Math.abs(dir),
18995         new_month, test;
18996         dir = dir > 0 ? 1 : -1;
18997         if (mag == 1){
18998             test = dir == -1
18999             // If going back one month, make sure month is not current month
19000             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19001             ? function(){
19002                 return new_date.getUTCMonth() == month;
19003             }
19004             // If going forward one month, make sure month is as expected
19005             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19006             : function(){
19007                 return new_date.getUTCMonth() != new_month;
19008             };
19009             new_month = month + dir;
19010             new_date.setUTCMonth(new_month);
19011             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19012             if (new_month < 0 || new_month > 11) {
19013                 new_month = (new_month + 12) % 12;
19014             }
19015         } else {
19016             // For magnitudes >1, move one month at a time...
19017             for (var i=0; i<mag; i++) {
19018                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19019                 new_date = this.moveMonth(new_date, dir);
19020             }
19021             // ...then reset the day, keeping it in the new month
19022             new_month = new_date.getUTCMonth();
19023             new_date.setUTCDate(day);
19024             test = function(){
19025                 return new_month != new_date.getUTCMonth();
19026             };
19027         }
19028         // Common date-resetting loop -- if date is beyond end of month, make it
19029         // end of month
19030         while (test()){
19031             new_date.setUTCDate(--day);
19032             new_date.setUTCMonth(new_month);
19033         }
19034         return new_date;
19035     },
19036
19037     moveYear: function(date, dir)
19038     {
19039         return this.moveMonth(date, dir*12);
19040     },
19041
19042     dateWithinRange: function(date)
19043     {
19044         return date >= this.startDate && date <= this.endDate;
19045     },
19046
19047     
19048     remove: function() 
19049     {
19050         this.picker().remove();
19051     },
19052     
19053     validateValue : function(value)
19054     {
19055         if(value.length < 1)  {
19056             if(this.allowBlank){
19057                 return true;
19058             }
19059             return false;
19060         }
19061         
19062         if(value.length < this.minLength){
19063             return false;
19064         }
19065         if(value.length > this.maxLength){
19066             return false;
19067         }
19068         if(this.vtype){
19069             var vt = Roo.form.VTypes;
19070             if(!vt[this.vtype](value, this)){
19071                 return false;
19072             }
19073         }
19074         if(typeof this.validator == "function"){
19075             var msg = this.validator(value);
19076             if(msg !== true){
19077                 return false;
19078             }
19079         }
19080         
19081         if(this.regex && !this.regex.test(value)){
19082             return false;
19083         }
19084         
19085         if(typeof(this.parseDate(value)) == 'undefined'){
19086             return false;
19087         }
19088         
19089         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19090             return false;
19091         }      
19092         
19093         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19094             return false;
19095         } 
19096         
19097         
19098         return true;
19099     }
19100    
19101 });
19102
19103 Roo.apply(Roo.bootstrap.DateField,  {
19104     
19105     head : {
19106         tag: 'thead',
19107         cn: [
19108         {
19109             tag: 'tr',
19110             cn: [
19111             {
19112                 tag: 'th',
19113                 cls: 'prev',
19114                 html: '<i class="fa fa-arrow-left"/>'
19115             },
19116             {
19117                 tag: 'th',
19118                 cls: 'switch',
19119                 colspan: '5'
19120             },
19121             {
19122                 tag: 'th',
19123                 cls: 'next',
19124                 html: '<i class="fa fa-arrow-right"/>'
19125             }
19126
19127             ]
19128         }
19129         ]
19130     },
19131     
19132     content : {
19133         tag: 'tbody',
19134         cn: [
19135         {
19136             tag: 'tr',
19137             cn: [
19138             {
19139                 tag: 'td',
19140                 colspan: '7'
19141             }
19142             ]
19143         }
19144         ]
19145     },
19146     
19147     footer : {
19148         tag: 'tfoot',
19149         cn: [
19150         {
19151             tag: 'tr',
19152             cn: [
19153             {
19154                 tag: 'th',
19155                 colspan: '7',
19156                 cls: 'today'
19157             }
19158                     
19159             ]
19160         }
19161         ]
19162     },
19163     
19164     dates:{
19165         en: {
19166             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19167             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19168             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19169             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19170             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19171             today: "Today"
19172         }
19173     },
19174     
19175     modes: [
19176     {
19177         clsName: 'days',
19178         navFnc: 'Month',
19179         navStep: 1
19180     },
19181     {
19182         clsName: 'months',
19183         navFnc: 'FullYear',
19184         navStep: 1
19185     },
19186     {
19187         clsName: 'years',
19188         navFnc: 'FullYear',
19189         navStep: 10
19190     }]
19191 });
19192
19193 Roo.apply(Roo.bootstrap.DateField,  {
19194   
19195     template : {
19196         tag: 'div',
19197         cls: 'datepicker dropdown-menu roo-dynamic',
19198         cn: [
19199         {
19200             tag: 'div',
19201             cls: 'datepicker-days',
19202             cn: [
19203             {
19204                 tag: 'table',
19205                 cls: 'table-condensed',
19206                 cn:[
19207                 Roo.bootstrap.DateField.head,
19208                 {
19209                     tag: 'tbody'
19210                 },
19211                 Roo.bootstrap.DateField.footer
19212                 ]
19213             }
19214             ]
19215         },
19216         {
19217             tag: 'div',
19218             cls: 'datepicker-months',
19219             cn: [
19220             {
19221                 tag: 'table',
19222                 cls: 'table-condensed',
19223                 cn:[
19224                 Roo.bootstrap.DateField.head,
19225                 Roo.bootstrap.DateField.content,
19226                 Roo.bootstrap.DateField.footer
19227                 ]
19228             }
19229             ]
19230         },
19231         {
19232             tag: 'div',
19233             cls: 'datepicker-years',
19234             cn: [
19235             {
19236                 tag: 'table',
19237                 cls: 'table-condensed',
19238                 cn:[
19239                 Roo.bootstrap.DateField.head,
19240                 Roo.bootstrap.DateField.content,
19241                 Roo.bootstrap.DateField.footer
19242                 ]
19243             }
19244             ]
19245         }
19246         ]
19247     }
19248 });
19249
19250  
19251
19252  /*
19253  * - LGPL
19254  *
19255  * TimeField
19256  * 
19257  */
19258
19259 /**
19260  * @class Roo.bootstrap.TimeField
19261  * @extends Roo.bootstrap.Input
19262  * Bootstrap DateField class
19263  * 
19264  * 
19265  * @constructor
19266  * Create a new TimeField
19267  * @param {Object} config The config object
19268  */
19269
19270 Roo.bootstrap.TimeField = function(config){
19271     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19272     this.addEvents({
19273             /**
19274              * @event show
19275              * Fires when this field show.
19276              * @param {Roo.bootstrap.DateField} thisthis
19277              * @param {Mixed} date The date value
19278              */
19279             show : true,
19280             /**
19281              * @event show
19282              * Fires when this field hide.
19283              * @param {Roo.bootstrap.DateField} this
19284              * @param {Mixed} date The date value
19285              */
19286             hide : true,
19287             /**
19288              * @event select
19289              * Fires when select a date.
19290              * @param {Roo.bootstrap.DateField} this
19291              * @param {Mixed} date The date value
19292              */
19293             select : true
19294         });
19295 };
19296
19297 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19298     
19299     /**
19300      * @cfg {String} format
19301      * The default time format string which can be overriden for localization support.  The format must be
19302      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19303      */
19304     format : "H:i",
19305        
19306     onRender: function(ct, position)
19307     {
19308         
19309         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19310                 
19311         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19312         
19313         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19314         
19315         this.pop = this.picker().select('>.datepicker-time',true).first();
19316         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19317         
19318         this.picker().on('mousedown', this.onMousedown, this);
19319         this.picker().on('click', this.onClick, this);
19320         
19321         this.picker().addClass('datepicker-dropdown');
19322     
19323         this.fillTime();
19324         this.update();
19325             
19326         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19327         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19328         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19329         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19330         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19331         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19332
19333     },
19334     
19335     fireKey: function(e){
19336         if (!this.picker().isVisible()){
19337             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19338                 this.show();
19339             }
19340             return;
19341         }
19342
19343         e.preventDefault();
19344         
19345         switch(e.keyCode){
19346             case 27: // escape
19347                 this.hide();
19348                 break;
19349             case 37: // left
19350             case 39: // right
19351                 this.onTogglePeriod();
19352                 break;
19353             case 38: // up
19354                 this.onIncrementMinutes();
19355                 break;
19356             case 40: // down
19357                 this.onDecrementMinutes();
19358                 break;
19359             case 13: // enter
19360             case 9: // tab
19361                 this.setTime();
19362                 break;
19363         }
19364     },
19365     
19366     onClick: function(e) {
19367         e.stopPropagation();
19368         e.preventDefault();
19369     },
19370     
19371     picker : function()
19372     {
19373         return this.el.select('.datepicker', true).first();
19374     },
19375     
19376     fillTime: function()
19377     {    
19378         var time = this.pop.select('tbody', true).first();
19379         
19380         time.dom.innerHTML = '';
19381         
19382         time.createChild({
19383             tag: 'tr',
19384             cn: [
19385                 {
19386                     tag: 'td',
19387                     cn: [
19388                         {
19389                             tag: 'a',
19390                             href: '#',
19391                             cls: 'btn',
19392                             cn: [
19393                                 {
19394                                     tag: 'span',
19395                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19396                                 }
19397                             ]
19398                         } 
19399                     ]
19400                 },
19401                 {
19402                     tag: 'td',
19403                     cls: 'separator'
19404                 },
19405                 {
19406                     tag: 'td',
19407                     cn: [
19408                         {
19409                             tag: 'a',
19410                             href: '#',
19411                             cls: 'btn',
19412                             cn: [
19413                                 {
19414                                     tag: 'span',
19415                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19416                                 }
19417                             ]
19418                         }
19419                     ]
19420                 },
19421                 {
19422                     tag: 'td',
19423                     cls: 'separator'
19424                 }
19425             ]
19426         });
19427         
19428         time.createChild({
19429             tag: 'tr',
19430             cn: [
19431                 {
19432                     tag: 'td',
19433                     cn: [
19434                         {
19435                             tag: 'span',
19436                             cls: 'timepicker-hour',
19437                             html: '00'
19438                         }  
19439                     ]
19440                 },
19441                 {
19442                     tag: 'td',
19443                     cls: 'separator',
19444                     html: ':'
19445                 },
19446                 {
19447                     tag: 'td',
19448                     cn: [
19449                         {
19450                             tag: 'span',
19451                             cls: 'timepicker-minute',
19452                             html: '00'
19453                         }  
19454                     ]
19455                 },
19456                 {
19457                     tag: 'td',
19458                     cls: 'separator'
19459                 },
19460                 {
19461                     tag: 'td',
19462                     cn: [
19463                         {
19464                             tag: 'button',
19465                             type: 'button',
19466                             cls: 'btn btn-primary period',
19467                             html: 'AM'
19468                             
19469                         }
19470                     ]
19471                 }
19472             ]
19473         });
19474         
19475         time.createChild({
19476             tag: 'tr',
19477             cn: [
19478                 {
19479                     tag: 'td',
19480                     cn: [
19481                         {
19482                             tag: 'a',
19483                             href: '#',
19484                             cls: 'btn',
19485                             cn: [
19486                                 {
19487                                     tag: 'span',
19488                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19489                                 }
19490                             ]
19491                         }
19492                     ]
19493                 },
19494                 {
19495                     tag: 'td',
19496                     cls: 'separator'
19497                 },
19498                 {
19499                     tag: 'td',
19500                     cn: [
19501                         {
19502                             tag: 'a',
19503                             href: '#',
19504                             cls: 'btn',
19505                             cn: [
19506                                 {
19507                                     tag: 'span',
19508                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19509                                 }
19510                             ]
19511                         }
19512                     ]
19513                 },
19514                 {
19515                     tag: 'td',
19516                     cls: 'separator'
19517                 }
19518             ]
19519         });
19520         
19521     },
19522     
19523     update: function()
19524     {
19525         
19526         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19527         
19528         this.fill();
19529     },
19530     
19531     fill: function() 
19532     {
19533         var hours = this.time.getHours();
19534         var minutes = this.time.getMinutes();
19535         var period = 'AM';
19536         
19537         if(hours > 11){
19538             period = 'PM';
19539         }
19540         
19541         if(hours == 0){
19542             hours = 12;
19543         }
19544         
19545         
19546         if(hours > 12){
19547             hours = hours - 12;
19548         }
19549         
19550         if(hours < 10){
19551             hours = '0' + hours;
19552         }
19553         
19554         if(minutes < 10){
19555             minutes = '0' + minutes;
19556         }
19557         
19558         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19559         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19560         this.pop.select('button', true).first().dom.innerHTML = period;
19561         
19562     },
19563     
19564     place: function()
19565     {   
19566         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19567         
19568         var cls = ['bottom'];
19569         
19570         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19571             cls.pop();
19572             cls.push('top');
19573         }
19574         
19575         cls.push('right');
19576         
19577         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19578             cls.pop();
19579             cls.push('left');
19580         }
19581         
19582         this.picker().addClass(cls.join('-'));
19583         
19584         var _this = this;
19585         
19586         Roo.each(cls, function(c){
19587             if(c == 'bottom'){
19588                 _this.picker().setTop(_this.inputEl().getHeight());
19589                 return;
19590             }
19591             if(c == 'top'){
19592                 _this.picker().setTop(0 - _this.picker().getHeight());
19593                 return;
19594             }
19595             
19596             if(c == 'left'){
19597                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19598                 return;
19599             }
19600             if(c == 'right'){
19601                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19602                 return;
19603             }
19604         });
19605         
19606     },
19607   
19608     onFocus : function()
19609     {
19610         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19611         this.show();
19612     },
19613     
19614     onBlur : function()
19615     {
19616         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19617         this.hide();
19618     },
19619     
19620     show : function()
19621     {
19622         this.picker().show();
19623         this.pop.show();
19624         this.update();
19625         this.place();
19626         
19627         this.fireEvent('show', this, this.date);
19628     },
19629     
19630     hide : function()
19631     {
19632         this.picker().hide();
19633         this.pop.hide();
19634         
19635         this.fireEvent('hide', this, this.date);
19636     },
19637     
19638     setTime : function()
19639     {
19640         this.hide();
19641         this.setValue(this.time.format(this.format));
19642         
19643         this.fireEvent('select', this, this.date);
19644         
19645         
19646     },
19647     
19648     onMousedown: function(e){
19649         e.stopPropagation();
19650         e.preventDefault();
19651     },
19652     
19653     onIncrementHours: function()
19654     {
19655         Roo.log('onIncrementHours');
19656         this.time = this.time.add(Date.HOUR, 1);
19657         this.update();
19658         
19659     },
19660     
19661     onDecrementHours: function()
19662     {
19663         Roo.log('onDecrementHours');
19664         this.time = this.time.add(Date.HOUR, -1);
19665         this.update();
19666     },
19667     
19668     onIncrementMinutes: function()
19669     {
19670         Roo.log('onIncrementMinutes');
19671         this.time = this.time.add(Date.MINUTE, 1);
19672         this.update();
19673     },
19674     
19675     onDecrementMinutes: function()
19676     {
19677         Roo.log('onDecrementMinutes');
19678         this.time = this.time.add(Date.MINUTE, -1);
19679         this.update();
19680     },
19681     
19682     onTogglePeriod: function()
19683     {
19684         Roo.log('onTogglePeriod');
19685         this.time = this.time.add(Date.HOUR, 12);
19686         this.update();
19687     }
19688     
19689    
19690 });
19691
19692 Roo.apply(Roo.bootstrap.TimeField,  {
19693     
19694     content : {
19695         tag: 'tbody',
19696         cn: [
19697             {
19698                 tag: 'tr',
19699                 cn: [
19700                 {
19701                     tag: 'td',
19702                     colspan: '7'
19703                 }
19704                 ]
19705             }
19706         ]
19707     },
19708     
19709     footer : {
19710         tag: 'tfoot',
19711         cn: [
19712             {
19713                 tag: 'tr',
19714                 cn: [
19715                 {
19716                     tag: 'th',
19717                     colspan: '7',
19718                     cls: '',
19719                     cn: [
19720                         {
19721                             tag: 'button',
19722                             cls: 'btn btn-info ok',
19723                             html: 'OK'
19724                         }
19725                     ]
19726                 }
19727
19728                 ]
19729             }
19730         ]
19731     }
19732 });
19733
19734 Roo.apply(Roo.bootstrap.TimeField,  {
19735   
19736     template : {
19737         tag: 'div',
19738         cls: 'datepicker dropdown-menu',
19739         cn: [
19740             {
19741                 tag: 'div',
19742                 cls: 'datepicker-time',
19743                 cn: [
19744                 {
19745                     tag: 'table',
19746                     cls: 'table-condensed',
19747                     cn:[
19748                     Roo.bootstrap.TimeField.content,
19749                     Roo.bootstrap.TimeField.footer
19750                     ]
19751                 }
19752                 ]
19753             }
19754         ]
19755     }
19756 });
19757
19758  
19759
19760  /*
19761  * - LGPL
19762  *
19763  * MonthField
19764  * 
19765  */
19766
19767 /**
19768  * @class Roo.bootstrap.MonthField
19769  * @extends Roo.bootstrap.Input
19770  * Bootstrap MonthField class
19771  * 
19772  * @cfg {String} language default en
19773  * 
19774  * @constructor
19775  * Create a new MonthField
19776  * @param {Object} config The config object
19777  */
19778
19779 Roo.bootstrap.MonthField = function(config){
19780     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19781     
19782     this.addEvents({
19783         /**
19784          * @event show
19785          * Fires when this field show.
19786          * @param {Roo.bootstrap.MonthField} this
19787          * @param {Mixed} date The date value
19788          */
19789         show : true,
19790         /**
19791          * @event show
19792          * Fires when this field hide.
19793          * @param {Roo.bootstrap.MonthField} this
19794          * @param {Mixed} date The date value
19795          */
19796         hide : true,
19797         /**
19798          * @event select
19799          * Fires when select a date.
19800          * @param {Roo.bootstrap.MonthField} this
19801          * @param {String} oldvalue The old value
19802          * @param {String} newvalue The new value
19803          */
19804         select : true
19805     });
19806 };
19807
19808 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19809     
19810     onRender: function(ct, position)
19811     {
19812         
19813         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19814         
19815         this.language = this.language || 'en';
19816         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19817         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19818         
19819         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19820         this.isInline = false;
19821         this.isInput = true;
19822         this.component = this.el.select('.add-on', true).first() || false;
19823         this.component = (this.component && this.component.length === 0) ? false : this.component;
19824         this.hasInput = this.component && this.inputEL().length;
19825         
19826         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19827         
19828         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19829         
19830         this.picker().on('mousedown', this.onMousedown, this);
19831         this.picker().on('click', this.onClick, this);
19832         
19833         this.picker().addClass('datepicker-dropdown');
19834         
19835         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19836             v.setStyle('width', '189px');
19837         });
19838         
19839         this.fillMonths();
19840         
19841         this.update();
19842         
19843         if(this.isInline) {
19844             this.show();
19845         }
19846         
19847     },
19848     
19849     setValue: function(v, suppressEvent)
19850     {   
19851         var o = this.getValue();
19852         
19853         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19854         
19855         this.update();
19856
19857         if(suppressEvent !== true){
19858             this.fireEvent('select', this, o, v);
19859         }
19860         
19861     },
19862     
19863     getValue: function()
19864     {
19865         return this.value;
19866     },
19867     
19868     onClick: function(e) 
19869     {
19870         e.stopPropagation();
19871         e.preventDefault();
19872         
19873         var target = e.getTarget();
19874         
19875         if(target.nodeName.toLowerCase() === 'i'){
19876             target = Roo.get(target).dom.parentNode;
19877         }
19878         
19879         var nodeName = target.nodeName;
19880         var className = target.className;
19881         var html = target.innerHTML;
19882         
19883         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19884             return;
19885         }
19886         
19887         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19888         
19889         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19890         
19891         this.hide();
19892                         
19893     },
19894     
19895     picker : function()
19896     {
19897         return this.pickerEl;
19898     },
19899     
19900     fillMonths: function()
19901     {    
19902         var i = 0;
19903         var months = this.picker().select('>.datepicker-months td', true).first();
19904         
19905         months.dom.innerHTML = '';
19906         
19907         while (i < 12) {
19908             var month = {
19909                 tag: 'span',
19910                 cls: 'month',
19911                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19912             };
19913             
19914             months.createChild(month);
19915         }
19916         
19917     },
19918     
19919     update: function()
19920     {
19921         var _this = this;
19922         
19923         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19924             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19925         }
19926         
19927         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19928             e.removeClass('active');
19929             
19930             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19931                 e.addClass('active');
19932             }
19933         })
19934     },
19935     
19936     place: function()
19937     {
19938         if(this.isInline) {
19939             return;
19940         }
19941         
19942         this.picker().removeClass(['bottom', 'top']);
19943         
19944         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19945             /*
19946              * place to the top of element!
19947              *
19948              */
19949             
19950             this.picker().addClass('top');
19951             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19952             
19953             return;
19954         }
19955         
19956         this.picker().addClass('bottom');
19957         
19958         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19959     },
19960     
19961     onFocus : function()
19962     {
19963         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19964         this.show();
19965     },
19966     
19967     onBlur : function()
19968     {
19969         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19970         
19971         var d = this.inputEl().getValue();
19972         
19973         this.setValue(d);
19974                 
19975         this.hide();
19976     },
19977     
19978     show : function()
19979     {
19980         this.picker().show();
19981         this.picker().select('>.datepicker-months', true).first().show();
19982         this.update();
19983         this.place();
19984         
19985         this.fireEvent('show', this, this.date);
19986     },
19987     
19988     hide : function()
19989     {
19990         if(this.isInline) {
19991             return;
19992         }
19993         this.picker().hide();
19994         this.fireEvent('hide', this, this.date);
19995         
19996     },
19997     
19998     onMousedown: function(e)
19999     {
20000         e.stopPropagation();
20001         e.preventDefault();
20002     },
20003     
20004     keyup: function(e)
20005     {
20006         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20007         this.update();
20008     },
20009
20010     fireKey: function(e)
20011     {
20012         if (!this.picker().isVisible()){
20013             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20014                 this.show();
20015             }
20016             return;
20017         }
20018         
20019         var dir;
20020         
20021         switch(e.keyCode){
20022             case 27: // escape
20023                 this.hide();
20024                 e.preventDefault();
20025                 break;
20026             case 37: // left
20027             case 39: // right
20028                 dir = e.keyCode == 37 ? -1 : 1;
20029                 
20030                 this.vIndex = this.vIndex + dir;
20031                 
20032                 if(this.vIndex < 0){
20033                     this.vIndex = 0;
20034                 }
20035                 
20036                 if(this.vIndex > 11){
20037                     this.vIndex = 11;
20038                 }
20039                 
20040                 if(isNaN(this.vIndex)){
20041                     this.vIndex = 0;
20042                 }
20043                 
20044                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20045                 
20046                 break;
20047             case 38: // up
20048             case 40: // down
20049                 
20050                 dir = e.keyCode == 38 ? -1 : 1;
20051                 
20052                 this.vIndex = this.vIndex + dir * 4;
20053                 
20054                 if(this.vIndex < 0){
20055                     this.vIndex = 0;
20056                 }
20057                 
20058                 if(this.vIndex > 11){
20059                     this.vIndex = 11;
20060                 }
20061                 
20062                 if(isNaN(this.vIndex)){
20063                     this.vIndex = 0;
20064                 }
20065                 
20066                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20067                 break;
20068                 
20069             case 13: // enter
20070                 
20071                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20072                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20073                 }
20074                 
20075                 this.hide();
20076                 e.preventDefault();
20077                 break;
20078             case 9: // tab
20079                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20080                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20081                 }
20082                 this.hide();
20083                 break;
20084             case 16: // shift
20085             case 17: // ctrl
20086             case 18: // alt
20087                 break;
20088             default :
20089                 this.hide();
20090                 
20091         }
20092     },
20093     
20094     remove: function() 
20095     {
20096         this.picker().remove();
20097     }
20098    
20099 });
20100
20101 Roo.apply(Roo.bootstrap.MonthField,  {
20102     
20103     content : {
20104         tag: 'tbody',
20105         cn: [
20106         {
20107             tag: 'tr',
20108             cn: [
20109             {
20110                 tag: 'td',
20111                 colspan: '7'
20112             }
20113             ]
20114         }
20115         ]
20116     },
20117     
20118     dates:{
20119         en: {
20120             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20121             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20122         }
20123     }
20124 });
20125
20126 Roo.apply(Roo.bootstrap.MonthField,  {
20127   
20128     template : {
20129         tag: 'div',
20130         cls: 'datepicker dropdown-menu roo-dynamic',
20131         cn: [
20132             {
20133                 tag: 'div',
20134                 cls: 'datepicker-months',
20135                 cn: [
20136                 {
20137                     tag: 'table',
20138                     cls: 'table-condensed',
20139                     cn:[
20140                         Roo.bootstrap.DateField.content
20141                     ]
20142                 }
20143                 ]
20144             }
20145         ]
20146     }
20147 });
20148
20149  
20150
20151  
20152  /*
20153  * - LGPL
20154  *
20155  * CheckBox
20156  * 
20157  */
20158
20159 /**
20160  * @class Roo.bootstrap.CheckBox
20161  * @extends Roo.bootstrap.Input
20162  * Bootstrap CheckBox class
20163  * 
20164  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20165  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20166  * @cfg {String} boxLabel The text that appears beside the checkbox
20167  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20168  * @cfg {Boolean} checked initnal the element
20169  * @cfg {Boolean} inline inline the element (default false)
20170  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20171  * @cfg {String} tooltip label tooltip
20172  * 
20173  * @constructor
20174  * Create a new CheckBox
20175  * @param {Object} config The config object
20176  */
20177
20178 Roo.bootstrap.CheckBox = function(config){
20179     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20180    
20181     this.addEvents({
20182         /**
20183         * @event check
20184         * Fires when the element is checked or unchecked.
20185         * @param {Roo.bootstrap.CheckBox} this This input
20186         * @param {Boolean} checked The new checked value
20187         */
20188        check : true,
20189        /**
20190         * @event click
20191         * Fires when the element is click.
20192         * @param {Roo.bootstrap.CheckBox} this This input
20193         */
20194        click : true
20195     });
20196     
20197 };
20198
20199 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20200   
20201     inputType: 'checkbox',
20202     inputValue: 1,
20203     valueOff: 0,
20204     boxLabel: false,
20205     checked: false,
20206     weight : false,
20207     inline: false,
20208     tooltip : '',
20209     
20210     getAutoCreate : function()
20211     {
20212         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20213         
20214         var id = Roo.id();
20215         
20216         var cfg = {};
20217         
20218         cfg.cls = 'form-group ' + this.inputType; //input-group
20219         
20220         if(this.inline){
20221             cfg.cls += ' ' + this.inputType + '-inline';
20222         }
20223         
20224         var input =  {
20225             tag: 'input',
20226             id : id,
20227             type : this.inputType,
20228             value : this.inputValue,
20229             cls : 'roo-' + this.inputType, //'form-box',
20230             placeholder : this.placeholder || ''
20231             
20232         };
20233         
20234         if(this.inputType != 'radio'){
20235             var hidden =  {
20236                 tag: 'input',
20237                 type : 'hidden',
20238                 cls : 'roo-hidden-value',
20239                 value : this.checked ? this.inputValue : this.valueOff
20240             };
20241         }
20242         
20243             
20244         if (this.weight) { // Validity check?
20245             cfg.cls += " " + this.inputType + "-" + this.weight;
20246         }
20247         
20248         if (this.disabled) {
20249             input.disabled=true;
20250         }
20251         
20252         if(this.checked){
20253             input.checked = this.checked;
20254         }
20255         
20256         if (this.name) {
20257             
20258             input.name = this.name;
20259             
20260             if(this.inputType != 'radio'){
20261                 hidden.name = this.name;
20262                 input.name = '_hidden_' + this.name;
20263             }
20264         }
20265         
20266         if (this.size) {
20267             input.cls += ' input-' + this.size;
20268         }
20269         
20270         var settings=this;
20271         
20272         ['xs','sm','md','lg'].map(function(size){
20273             if (settings[size]) {
20274                 cfg.cls += ' col-' + size + '-' + settings[size];
20275             }
20276         });
20277         
20278         var inputblock = input;
20279          
20280         if (this.before || this.after) {
20281             
20282             inputblock = {
20283                 cls : 'input-group',
20284                 cn :  [] 
20285             };
20286             
20287             if (this.before) {
20288                 inputblock.cn.push({
20289                     tag :'span',
20290                     cls : 'input-group-addon',
20291                     html : this.before
20292                 });
20293             }
20294             
20295             inputblock.cn.push(input);
20296             
20297             if(this.inputType != 'radio'){
20298                 inputblock.cn.push(hidden);
20299             }
20300             
20301             if (this.after) {
20302                 inputblock.cn.push({
20303                     tag :'span',
20304                     cls : 'input-group-addon',
20305                     html : this.after
20306                 });
20307             }
20308             
20309         }
20310         
20311         if (align ==='left' && this.fieldLabel.length) {
20312 //                Roo.log("left and has label");
20313             cfg.cn = [
20314                 {
20315                     tag: 'label',
20316                     'for' :  id,
20317                     cls : 'control-label',
20318                     html : this.fieldLabel
20319                 },
20320                 {
20321                     cls : "", 
20322                     cn: [
20323                         inputblock
20324                     ]
20325                 }
20326             ];
20327             
20328             if(this.labelWidth > 12){
20329                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20330             }
20331             
20332             if(this.labelWidth < 13 && this.labelmd == 0){
20333                 this.labelmd = this.labelWidth;
20334             }
20335             
20336             if(this.labellg > 0){
20337                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20338                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20339             }
20340             
20341             if(this.labelmd > 0){
20342                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20343                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20344             }
20345             
20346             if(this.labelsm > 0){
20347                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20348                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20349             }
20350             
20351             if(this.labelxs > 0){
20352                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20353                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20354             }
20355             
20356         } else if ( this.fieldLabel.length) {
20357 //                Roo.log(" label");
20358                 cfg.cn = [
20359                    
20360                     {
20361                         tag: this.boxLabel ? 'span' : 'label',
20362                         'for': id,
20363                         cls: 'control-label box-input-label',
20364                         //cls : 'input-group-addon',
20365                         html : this.fieldLabel
20366                     },
20367                     
20368                     inputblock
20369                     
20370                 ];
20371
20372         } else {
20373             
20374 //                Roo.log(" no label && no align");
20375                 cfg.cn = [  inputblock ] ;
20376                 
20377                 
20378         }
20379         
20380         if(this.boxLabel){
20381              var boxLabelCfg = {
20382                 tag: 'label',
20383                 //'for': id, // box label is handled by onclick - so no for...
20384                 cls: 'box-label',
20385                 html: this.boxLabel
20386             };
20387             
20388             if(this.tooltip){
20389                 boxLabelCfg.tooltip = this.tooltip;
20390             }
20391              
20392             cfg.cn.push(boxLabelCfg);
20393         }
20394         
20395         if(this.inputType != 'radio'){
20396             cfg.cn.push(hidden);
20397         }
20398         
20399         return cfg;
20400         
20401     },
20402     
20403     /**
20404      * return the real input element.
20405      */
20406     inputEl: function ()
20407     {
20408         return this.el.select('input.roo-' + this.inputType,true).first();
20409     },
20410     hiddenEl: function ()
20411     {
20412         return this.el.select('input.roo-hidden-value',true).first();
20413     },
20414     
20415     labelEl: function()
20416     {
20417         return this.el.select('label.control-label',true).first();
20418     },
20419     /* depricated... */
20420     
20421     label: function()
20422     {
20423         return this.labelEl();
20424     },
20425     
20426     boxLabelEl: function()
20427     {
20428         return this.el.select('label.box-label',true).first();
20429     },
20430     
20431     initEvents : function()
20432     {
20433 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20434         
20435         this.inputEl().on('click', this.onClick,  this);
20436         
20437         if (this.boxLabel) { 
20438             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20439         }
20440         
20441         this.startValue = this.getValue();
20442         
20443         if(this.groupId){
20444             Roo.bootstrap.CheckBox.register(this);
20445         }
20446     },
20447     
20448     onClick : function(e)
20449     {   
20450         if(this.fireEvent('click', this, e) !== false){
20451             this.setChecked(!this.checked);
20452         }
20453         
20454     },
20455     
20456     setChecked : function(state,suppressEvent)
20457     {
20458         this.startValue = this.getValue();
20459
20460         if(this.inputType == 'radio'){
20461             
20462             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20463                 e.dom.checked = false;
20464             });
20465             
20466             this.inputEl().dom.checked = true;
20467             
20468             this.inputEl().dom.value = this.inputValue;
20469             
20470             if(suppressEvent !== true){
20471                 this.fireEvent('check', this, true);
20472             }
20473             
20474             this.validate();
20475             
20476             return;
20477         }
20478         
20479         this.checked = state;
20480         
20481         this.inputEl().dom.checked = state;
20482         
20483         
20484         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20485         
20486         if(suppressEvent !== true){
20487             this.fireEvent('check', this, state);
20488         }
20489         
20490         this.validate();
20491     },
20492     
20493     getValue : function()
20494     {
20495         if(this.inputType == 'radio'){
20496             return this.getGroupValue();
20497         }
20498         
20499         return this.hiddenEl().dom.value;
20500         
20501     },
20502     
20503     getGroupValue : function()
20504     {
20505         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20506             return '';
20507         }
20508         
20509         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20510     },
20511     
20512     setValue : function(v,suppressEvent)
20513     {
20514         if(this.inputType == 'radio'){
20515             this.setGroupValue(v, suppressEvent);
20516             return;
20517         }
20518         
20519         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20520         
20521         this.validate();
20522     },
20523     
20524     setGroupValue : function(v, suppressEvent)
20525     {
20526         this.startValue = this.getValue();
20527         
20528         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20529             e.dom.checked = false;
20530             
20531             if(e.dom.value == v){
20532                 e.dom.checked = true;
20533             }
20534         });
20535         
20536         if(suppressEvent !== true){
20537             this.fireEvent('check', this, true);
20538         }
20539
20540         this.validate();
20541         
20542         return;
20543     },
20544     
20545     validate : function()
20546     {
20547         if(
20548                 this.disabled || 
20549                 (this.inputType == 'radio' && this.validateRadio()) ||
20550                 (this.inputType == 'checkbox' && this.validateCheckbox())
20551         ){
20552             this.markValid();
20553             return true;
20554         }
20555         
20556         this.markInvalid();
20557         return false;
20558     },
20559     
20560     validateRadio : function()
20561     {
20562         if(this.allowBlank){
20563             return true;
20564         }
20565         
20566         var valid = false;
20567         
20568         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20569             if(!e.dom.checked){
20570                 return;
20571             }
20572             
20573             valid = true;
20574             
20575             return false;
20576         });
20577         
20578         return valid;
20579     },
20580     
20581     validateCheckbox : function()
20582     {
20583         if(!this.groupId){
20584             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20585             //return (this.getValue() == this.inputValue) ? true : false;
20586         }
20587         
20588         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20589         
20590         if(!group){
20591             return false;
20592         }
20593         
20594         var r = false;
20595         
20596         for(var i in group){
20597             if(group[i].el.isVisible(true)){
20598                 r = false;
20599                 break;
20600             }
20601             
20602             r = true;
20603         }
20604         
20605         for(var i in group){
20606             if(r){
20607                 break;
20608             }
20609             
20610             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20611         }
20612         
20613         return r;
20614     },
20615     
20616     /**
20617      * Mark this field as valid
20618      */
20619     markValid : function()
20620     {
20621         var _this = this;
20622         
20623         this.fireEvent('valid', this);
20624         
20625         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20626         
20627         if(this.groupId){
20628             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20629         }
20630         
20631         if(label){
20632             label.markValid();
20633         }
20634
20635         if(this.inputType == 'radio'){
20636             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20637                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20638                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20639             });
20640             
20641             return;
20642         }
20643
20644         if(!this.groupId){
20645             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20646             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20647             return;
20648         }
20649         
20650         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20651         
20652         if(!group){
20653             return;
20654         }
20655         
20656         for(var i in group){
20657             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20658             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20659         }
20660     },
20661     
20662      /**
20663      * Mark this field as invalid
20664      * @param {String} msg The validation message
20665      */
20666     markInvalid : function(msg)
20667     {
20668         if(this.allowBlank){
20669             return;
20670         }
20671         
20672         var _this = this;
20673         
20674         this.fireEvent('invalid', this, msg);
20675         
20676         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20677         
20678         if(this.groupId){
20679             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20680         }
20681         
20682         if(label){
20683             label.markInvalid();
20684         }
20685             
20686         if(this.inputType == 'radio'){
20687             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20688                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20689                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20690             });
20691             
20692             return;
20693         }
20694         
20695         if(!this.groupId){
20696             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20697             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20698             return;
20699         }
20700         
20701         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20702         
20703         if(!group){
20704             return;
20705         }
20706         
20707         for(var i in group){
20708             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20709             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20710         }
20711         
20712     },
20713     
20714     clearInvalid : function()
20715     {
20716         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20717         
20718         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20719         
20720         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20721         
20722         if (label && label.iconEl) {
20723             label.iconEl.removeClass(label.validClass);
20724             label.iconEl.removeClass(label.invalidClass);
20725         }
20726     },
20727     
20728     disable : function()
20729     {
20730         if(this.inputType != 'radio'){
20731             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20732             return;
20733         }
20734         
20735         var _this = this;
20736         
20737         if(this.rendered){
20738             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20739                 _this.getActionEl().addClass(this.disabledClass);
20740                 e.dom.disabled = true;
20741             });
20742         }
20743         
20744         this.disabled = true;
20745         this.fireEvent("disable", this);
20746         return this;
20747     },
20748
20749     enable : function()
20750     {
20751         if(this.inputType != 'radio'){
20752             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20753             return;
20754         }
20755         
20756         var _this = this;
20757         
20758         if(this.rendered){
20759             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20760                 _this.getActionEl().removeClass(this.disabledClass);
20761                 e.dom.disabled = false;
20762             });
20763         }
20764         
20765         this.disabled = false;
20766         this.fireEvent("enable", this);
20767         return this;
20768     },
20769     
20770     setBoxLabel : function(v)
20771     {
20772         this.boxLabel = v;
20773         
20774         if(this.rendered){
20775             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20776         }
20777     }
20778
20779 });
20780
20781 Roo.apply(Roo.bootstrap.CheckBox, {
20782     
20783     groups: {},
20784     
20785      /**
20786     * register a CheckBox Group
20787     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20788     */
20789     register : function(checkbox)
20790     {
20791         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20792             this.groups[checkbox.groupId] = {};
20793         }
20794         
20795         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20796             return;
20797         }
20798         
20799         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20800         
20801     },
20802     /**
20803     * fetch a CheckBox Group based on the group ID
20804     * @param {string} the group ID
20805     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20806     */
20807     get: function(groupId) {
20808         if (typeof(this.groups[groupId]) == 'undefined') {
20809             return false;
20810         }
20811         
20812         return this.groups[groupId] ;
20813     }
20814     
20815     
20816 });
20817 /*
20818  * - LGPL
20819  *
20820  * RadioItem
20821  * 
20822  */
20823
20824 /**
20825  * @class Roo.bootstrap.Radio
20826  * @extends Roo.bootstrap.Component
20827  * Bootstrap Radio class
20828  * @cfg {String} boxLabel - the label associated
20829  * @cfg {String} value - the value of radio
20830  * 
20831  * @constructor
20832  * Create a new Radio
20833  * @param {Object} config The config object
20834  */
20835 Roo.bootstrap.Radio = function(config){
20836     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20837     
20838 };
20839
20840 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20841     
20842     boxLabel : '',
20843     
20844     value : '',
20845     
20846     getAutoCreate : function()
20847     {
20848         var cfg = {
20849             tag : 'div',
20850             cls : 'form-group radio',
20851             cn : [
20852                 {
20853                     tag : 'label',
20854                     cls : 'box-label',
20855                     html : this.boxLabel
20856                 }
20857             ]
20858         };
20859         
20860         return cfg;
20861     },
20862     
20863     initEvents : function() 
20864     {
20865         this.parent().register(this);
20866         
20867         this.el.on('click', this.onClick, this);
20868         
20869     },
20870     
20871     onClick : function()
20872     {
20873         this.setChecked(true);
20874     },
20875     
20876     setChecked : function(state, suppressEvent)
20877     {
20878         this.parent().setValue(this.value, suppressEvent);
20879         
20880     },
20881     
20882     setBoxLabel : function(v)
20883     {
20884         this.boxLabel = v;
20885         
20886         if(this.rendered){
20887             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20888         }
20889     }
20890     
20891 });
20892  
20893
20894  /*
20895  * - LGPL
20896  *
20897  * Input
20898  * 
20899  */
20900
20901 /**
20902  * @class Roo.bootstrap.SecurePass
20903  * @extends Roo.bootstrap.Input
20904  * Bootstrap SecurePass class
20905  *
20906  * 
20907  * @constructor
20908  * Create a new SecurePass
20909  * @param {Object} config The config object
20910  */
20911  
20912 Roo.bootstrap.SecurePass = function (config) {
20913     // these go here, so the translation tool can replace them..
20914     this.errors = {
20915         PwdEmpty: "Please type a password, and then retype it to confirm.",
20916         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20917         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20918         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20919         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20920         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20921         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20922         TooWeak: "Your password is Too Weak."
20923     },
20924     this.meterLabel = "Password strength:";
20925     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20926     this.meterClass = [
20927         "roo-password-meter-tooweak", 
20928         "roo-password-meter-weak", 
20929         "roo-password-meter-medium", 
20930         "roo-password-meter-strong", 
20931         "roo-password-meter-grey"
20932     ];
20933     
20934     this.errors = {};
20935     
20936     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20937 }
20938
20939 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20940     /**
20941      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20942      * {
20943      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20944      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20945      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20946      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20947      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20948      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20949      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20950      * })
20951      */
20952     // private
20953     
20954     meterWidth: 300,
20955     errorMsg :'',    
20956     errors: false,
20957     imageRoot: '/',
20958     /**
20959      * @cfg {String/Object} Label for the strength meter (defaults to
20960      * 'Password strength:')
20961      */
20962     // private
20963     meterLabel: '',
20964     /**
20965      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20966      * ['Weak', 'Medium', 'Strong'])
20967      */
20968     // private    
20969     pwdStrengths: false,    
20970     // private
20971     strength: 0,
20972     // private
20973     _lastPwd: null,
20974     // private
20975     kCapitalLetter: 0,
20976     kSmallLetter: 1,
20977     kDigit: 2,
20978     kPunctuation: 3,
20979     
20980     insecure: false,
20981     // private
20982     initEvents: function ()
20983     {
20984         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20985
20986         if (this.el.is('input[type=password]') && Roo.isSafari) {
20987             this.el.on('keydown', this.SafariOnKeyDown, this);
20988         }
20989
20990         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20991     },
20992     // private
20993     onRender: function (ct, position)
20994     {
20995         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20996         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20997         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20998
20999         this.trigger.createChild({
21000                    cn: [
21001                     {
21002                     //id: 'PwdMeter',
21003                     tag: 'div',
21004                     cls: 'roo-password-meter-grey col-xs-12',
21005                     style: {
21006                         //width: 0,
21007                         //width: this.meterWidth + 'px'                                                
21008                         }
21009                     },
21010                     {                            
21011                          cls: 'roo-password-meter-text'                          
21012                     }
21013                 ]            
21014         });
21015
21016          
21017         if (this.hideTrigger) {
21018             this.trigger.setDisplayed(false);
21019         }
21020         this.setSize(this.width || '', this.height || '');
21021     },
21022     // private
21023     onDestroy: function ()
21024     {
21025         if (this.trigger) {
21026             this.trigger.removeAllListeners();
21027             this.trigger.remove();
21028         }
21029         if (this.wrap) {
21030             this.wrap.remove();
21031         }
21032         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21033     },
21034     // private
21035     checkStrength: function ()
21036     {
21037         var pwd = this.inputEl().getValue();
21038         if (pwd == this._lastPwd) {
21039             return;
21040         }
21041
21042         var strength;
21043         if (this.ClientSideStrongPassword(pwd)) {
21044             strength = 3;
21045         } else if (this.ClientSideMediumPassword(pwd)) {
21046             strength = 2;
21047         } else if (this.ClientSideWeakPassword(pwd)) {
21048             strength = 1;
21049         } else {
21050             strength = 0;
21051         }
21052         
21053         Roo.log('strength1: ' + strength);
21054         
21055         //var pm = this.trigger.child('div/div/div').dom;
21056         var pm = this.trigger.child('div/div');
21057         pm.removeClass(this.meterClass);
21058         pm.addClass(this.meterClass[strength]);
21059                 
21060         
21061         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21062                 
21063         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21064         
21065         this._lastPwd = pwd;
21066     },
21067     reset: function ()
21068     {
21069         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21070         
21071         this._lastPwd = '';
21072         
21073         var pm = this.trigger.child('div/div');
21074         pm.removeClass(this.meterClass);
21075         pm.addClass('roo-password-meter-grey');        
21076         
21077         
21078         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21079         
21080         pt.innerHTML = '';
21081         this.inputEl().dom.type='password';
21082     },
21083     // private
21084     validateValue: function (value)
21085     {
21086         
21087         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21088             return false;
21089         }
21090         if (value.length == 0) {
21091             if (this.allowBlank) {
21092                 this.clearInvalid();
21093                 return true;
21094             }
21095
21096             this.markInvalid(this.errors.PwdEmpty);
21097             this.errorMsg = this.errors.PwdEmpty;
21098             return false;
21099         }
21100         
21101         if(this.insecure){
21102             return true;
21103         }
21104         
21105         if ('[\x21-\x7e]*'.match(value)) {
21106             this.markInvalid(this.errors.PwdBadChar);
21107             this.errorMsg = this.errors.PwdBadChar;
21108             return false;
21109         }
21110         if (value.length < 6) {
21111             this.markInvalid(this.errors.PwdShort);
21112             this.errorMsg = this.errors.PwdShort;
21113             return false;
21114         }
21115         if (value.length > 16) {
21116             this.markInvalid(this.errors.PwdLong);
21117             this.errorMsg = this.errors.PwdLong;
21118             return false;
21119         }
21120         var strength;
21121         if (this.ClientSideStrongPassword(value)) {
21122             strength = 3;
21123         } else if (this.ClientSideMediumPassword(value)) {
21124             strength = 2;
21125         } else if (this.ClientSideWeakPassword(value)) {
21126             strength = 1;
21127         } else {
21128             strength = 0;
21129         }
21130
21131         
21132         if (strength < 2) {
21133             //this.markInvalid(this.errors.TooWeak);
21134             this.errorMsg = this.errors.TooWeak;
21135             //return false;
21136         }
21137         
21138         
21139         console.log('strength2: ' + strength);
21140         
21141         //var pm = this.trigger.child('div/div/div').dom;
21142         
21143         var pm = this.trigger.child('div/div');
21144         pm.removeClass(this.meterClass);
21145         pm.addClass(this.meterClass[strength]);
21146                 
21147         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21148                 
21149         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21150         
21151         this.errorMsg = ''; 
21152         return true;
21153     },
21154     // private
21155     CharacterSetChecks: function (type)
21156     {
21157         this.type = type;
21158         this.fResult = false;
21159     },
21160     // private
21161     isctype: function (character, type)
21162     {
21163         switch (type) {  
21164             case this.kCapitalLetter:
21165                 if (character >= 'A' && character <= 'Z') {
21166                     return true;
21167                 }
21168                 break;
21169             
21170             case this.kSmallLetter:
21171                 if (character >= 'a' && character <= 'z') {
21172                     return true;
21173                 }
21174                 break;
21175             
21176             case this.kDigit:
21177                 if (character >= '0' && character <= '9') {
21178                     return true;
21179                 }
21180                 break;
21181             
21182             case this.kPunctuation:
21183                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21184                     return true;
21185                 }
21186                 break;
21187             
21188             default:
21189                 return false;
21190         }
21191
21192     },
21193     // private
21194     IsLongEnough: function (pwd, size)
21195     {
21196         return !(pwd == null || isNaN(size) || pwd.length < size);
21197     },
21198     // private
21199     SpansEnoughCharacterSets: function (word, nb)
21200     {
21201         if (!this.IsLongEnough(word, nb))
21202         {
21203             return false;
21204         }
21205
21206         var characterSetChecks = new Array(
21207             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21208             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21209         );
21210         
21211         for (var index = 0; index < word.length; ++index) {
21212             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21213                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21214                     characterSetChecks[nCharSet].fResult = true;
21215                     break;
21216                 }
21217             }
21218         }
21219
21220         var nCharSets = 0;
21221         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21222             if (characterSetChecks[nCharSet].fResult) {
21223                 ++nCharSets;
21224             }
21225         }
21226
21227         if (nCharSets < nb) {
21228             return false;
21229         }
21230         return true;
21231     },
21232     // private
21233     ClientSideStrongPassword: function (pwd)
21234     {
21235         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21236     },
21237     // private
21238     ClientSideMediumPassword: function (pwd)
21239     {
21240         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21241     },
21242     // private
21243     ClientSideWeakPassword: function (pwd)
21244     {
21245         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21246     }
21247           
21248 })//<script type="text/javascript">
21249
21250 /*
21251  * Based  Ext JS Library 1.1.1
21252  * Copyright(c) 2006-2007, Ext JS, LLC.
21253  * LGPL
21254  *
21255  */
21256  
21257 /**
21258  * @class Roo.HtmlEditorCore
21259  * @extends Roo.Component
21260  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21261  *
21262  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21263  */
21264
21265 Roo.HtmlEditorCore = function(config){
21266     
21267     
21268     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21269     
21270     
21271     this.addEvents({
21272         /**
21273          * @event initialize
21274          * Fires when the editor is fully initialized (including the iframe)
21275          * @param {Roo.HtmlEditorCore} this
21276          */
21277         initialize: true,
21278         /**
21279          * @event activate
21280          * Fires when the editor is first receives the focus. Any insertion must wait
21281          * until after this event.
21282          * @param {Roo.HtmlEditorCore} this
21283          */
21284         activate: true,
21285          /**
21286          * @event beforesync
21287          * Fires before the textarea is updated with content from the editor iframe. Return false
21288          * to cancel the sync.
21289          * @param {Roo.HtmlEditorCore} this
21290          * @param {String} html
21291          */
21292         beforesync: true,
21293          /**
21294          * @event beforepush
21295          * Fires before the iframe editor is updated with content from the textarea. Return false
21296          * to cancel the push.
21297          * @param {Roo.HtmlEditorCore} this
21298          * @param {String} html
21299          */
21300         beforepush: true,
21301          /**
21302          * @event sync
21303          * Fires when the textarea is updated with content from the editor iframe.
21304          * @param {Roo.HtmlEditorCore} this
21305          * @param {String} html
21306          */
21307         sync: true,
21308          /**
21309          * @event push
21310          * Fires when the iframe editor is updated with content from the textarea.
21311          * @param {Roo.HtmlEditorCore} this
21312          * @param {String} html
21313          */
21314         push: true,
21315         
21316         /**
21317          * @event editorevent
21318          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21319          * @param {Roo.HtmlEditorCore} this
21320          */
21321         editorevent: true
21322         
21323     });
21324     
21325     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21326     
21327     // defaults : white / black...
21328     this.applyBlacklists();
21329     
21330     
21331     
21332 };
21333
21334
21335 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21336
21337
21338      /**
21339      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21340      */
21341     
21342     owner : false,
21343     
21344      /**
21345      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21346      *                        Roo.resizable.
21347      */
21348     resizable : false,
21349      /**
21350      * @cfg {Number} height (in pixels)
21351      */   
21352     height: 300,
21353    /**
21354      * @cfg {Number} width (in pixels)
21355      */   
21356     width: 500,
21357     
21358     /**
21359      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21360      * 
21361      */
21362     stylesheets: false,
21363     
21364     // id of frame..
21365     frameId: false,
21366     
21367     // private properties
21368     validationEvent : false,
21369     deferHeight: true,
21370     initialized : false,
21371     activated : false,
21372     sourceEditMode : false,
21373     onFocus : Roo.emptyFn,
21374     iframePad:3,
21375     hideMode:'offsets',
21376     
21377     clearUp: true,
21378     
21379     // blacklist + whitelisted elements..
21380     black: false,
21381     white: false,
21382      
21383     bodyCls : '',
21384
21385     /**
21386      * Protected method that will not generally be called directly. It
21387      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21388      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21389      */
21390     getDocMarkup : function(){
21391         // body styles..
21392         var st = '';
21393         
21394         // inherit styels from page...?? 
21395         if (this.stylesheets === false) {
21396             
21397             Roo.get(document.head).select('style').each(function(node) {
21398                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21399             });
21400             
21401             Roo.get(document.head).select('link').each(function(node) { 
21402                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21403             });
21404             
21405         } else if (!this.stylesheets.length) {
21406                 // simple..
21407                 st = '<style type="text/css">' +
21408                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21409                    '</style>';
21410         } else { 
21411             st = '<style type="text/css">' +
21412                     this.stylesheets +
21413                 '</style>';
21414         }
21415         
21416         st +=  '<style type="text/css">' +
21417             'IMG { cursor: pointer } ' +
21418         '</style>';
21419
21420         var cls = 'roo-htmleditor-body';
21421         
21422         if(this.bodyCls.length){
21423             cls += ' ' + this.bodyCls;
21424         }
21425         
21426         return '<html><head>' + st  +
21427             //<style type="text/css">' +
21428             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21429             //'</style>' +
21430             ' </head><body class="' +  cls + '"></body></html>';
21431     },
21432
21433     // private
21434     onRender : function(ct, position)
21435     {
21436         var _t = this;
21437         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21438         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21439         
21440         
21441         this.el.dom.style.border = '0 none';
21442         this.el.dom.setAttribute('tabIndex', -1);
21443         this.el.addClass('x-hidden hide');
21444         
21445         
21446         
21447         if(Roo.isIE){ // fix IE 1px bogus margin
21448             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21449         }
21450        
21451         
21452         this.frameId = Roo.id();
21453         
21454          
21455         
21456         var iframe = this.owner.wrap.createChild({
21457             tag: 'iframe',
21458             cls: 'form-control', // bootstrap..
21459             id: this.frameId,
21460             name: this.frameId,
21461             frameBorder : 'no',
21462             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21463         }, this.el
21464         );
21465         
21466         
21467         this.iframe = iframe.dom;
21468
21469          this.assignDocWin();
21470         
21471         this.doc.designMode = 'on';
21472        
21473         this.doc.open();
21474         this.doc.write(this.getDocMarkup());
21475         this.doc.close();
21476
21477         
21478         var task = { // must defer to wait for browser to be ready
21479             run : function(){
21480                 //console.log("run task?" + this.doc.readyState);
21481                 this.assignDocWin();
21482                 if(this.doc.body || this.doc.readyState == 'complete'){
21483                     try {
21484                         this.doc.designMode="on";
21485                     } catch (e) {
21486                         return;
21487                     }
21488                     Roo.TaskMgr.stop(task);
21489                     this.initEditor.defer(10, this);
21490                 }
21491             },
21492             interval : 10,
21493             duration: 10000,
21494             scope: this
21495         };
21496         Roo.TaskMgr.start(task);
21497
21498     },
21499
21500     // private
21501     onResize : function(w, h)
21502     {
21503          Roo.log('resize: ' +w + ',' + h );
21504         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21505         if(!this.iframe){
21506             return;
21507         }
21508         if(typeof w == 'number'){
21509             
21510             this.iframe.style.width = w + 'px';
21511         }
21512         if(typeof h == 'number'){
21513             
21514             this.iframe.style.height = h + 'px';
21515             if(this.doc){
21516                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21517             }
21518         }
21519         
21520     },
21521
21522     /**
21523      * Toggles the editor between standard and source edit mode.
21524      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21525      */
21526     toggleSourceEdit : function(sourceEditMode){
21527         
21528         this.sourceEditMode = sourceEditMode === true;
21529         
21530         if(this.sourceEditMode){
21531  
21532             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21533             
21534         }else{
21535             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21536             //this.iframe.className = '';
21537             this.deferFocus();
21538         }
21539         //this.setSize(this.owner.wrap.getSize());
21540         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21541     },
21542
21543     
21544   
21545
21546     /**
21547      * Protected method that will not generally be called directly. If you need/want
21548      * custom HTML cleanup, this is the method you should override.
21549      * @param {String} html The HTML to be cleaned
21550      * return {String} The cleaned HTML
21551      */
21552     cleanHtml : function(html){
21553         html = String(html);
21554         if(html.length > 5){
21555             if(Roo.isSafari){ // strip safari nonsense
21556                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21557             }
21558         }
21559         if(html == '&nbsp;'){
21560             html = '';
21561         }
21562         return html;
21563     },
21564
21565     /**
21566      * HTML Editor -> Textarea
21567      * Protected method that will not generally be called directly. Syncs the contents
21568      * of the editor iframe with the textarea.
21569      */
21570     syncValue : function(){
21571         if(this.initialized){
21572             var bd = (this.doc.body || this.doc.documentElement);
21573             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21574             var html = bd.innerHTML;
21575             if(Roo.isSafari){
21576                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21577                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21578                 if(m && m[1]){
21579                     html = '<div style="'+m[0]+'">' + html + '</div>';
21580                 }
21581             }
21582             html = this.cleanHtml(html);
21583             // fix up the special chars.. normaly like back quotes in word...
21584             // however we do not want to do this with chinese..
21585             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21586                 var cc = b.charCodeAt();
21587                 if (
21588                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21589                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21590                     (cc >= 0xf900 && cc < 0xfb00 )
21591                 ) {
21592                         return b;
21593                 }
21594                 return "&#"+cc+";" 
21595             });
21596             if(this.owner.fireEvent('beforesync', this, html) !== false){
21597                 this.el.dom.value = html;
21598                 this.owner.fireEvent('sync', this, html);
21599             }
21600         }
21601     },
21602
21603     /**
21604      * Protected method that will not generally be called directly. Pushes the value of the textarea
21605      * into the iframe editor.
21606      */
21607     pushValue : function(){
21608         if(this.initialized){
21609             var v = this.el.dom.value.trim();
21610             
21611 //            if(v.length < 1){
21612 //                v = '&#160;';
21613 //            }
21614             
21615             if(this.owner.fireEvent('beforepush', this, v) !== false){
21616                 var d = (this.doc.body || this.doc.documentElement);
21617                 d.innerHTML = v;
21618                 this.cleanUpPaste();
21619                 this.el.dom.value = d.innerHTML;
21620                 this.owner.fireEvent('push', this, v);
21621             }
21622         }
21623     },
21624
21625     // private
21626     deferFocus : function(){
21627         this.focus.defer(10, this);
21628     },
21629
21630     // doc'ed in Field
21631     focus : function(){
21632         if(this.win && !this.sourceEditMode){
21633             this.win.focus();
21634         }else{
21635             this.el.focus();
21636         }
21637     },
21638     
21639     assignDocWin: function()
21640     {
21641         var iframe = this.iframe;
21642         
21643          if(Roo.isIE){
21644             this.doc = iframe.contentWindow.document;
21645             this.win = iframe.contentWindow;
21646         } else {
21647 //            if (!Roo.get(this.frameId)) {
21648 //                return;
21649 //            }
21650 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21651 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21652             
21653             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21654                 return;
21655             }
21656             
21657             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21658             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21659         }
21660     },
21661     
21662     // private
21663     initEditor : function(){
21664         //console.log("INIT EDITOR");
21665         this.assignDocWin();
21666         
21667         
21668         
21669         this.doc.designMode="on";
21670         this.doc.open();
21671         this.doc.write(this.getDocMarkup());
21672         this.doc.close();
21673         
21674         var dbody = (this.doc.body || this.doc.documentElement);
21675         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21676         // this copies styles from the containing element into thsi one..
21677         // not sure why we need all of this..
21678         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21679         
21680         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21681         //ss['background-attachment'] = 'fixed'; // w3c
21682         dbody.bgProperties = 'fixed'; // ie
21683         //Roo.DomHelper.applyStyles(dbody, ss);
21684         Roo.EventManager.on(this.doc, {
21685             //'mousedown': this.onEditorEvent,
21686             'mouseup': this.onEditorEvent,
21687             'dblclick': this.onEditorEvent,
21688             'click': this.onEditorEvent,
21689             'keyup': this.onEditorEvent,
21690             buffer:100,
21691             scope: this
21692         });
21693         if(Roo.isGecko){
21694             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21695         }
21696         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21697             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21698         }
21699         this.initialized = true;
21700
21701         this.owner.fireEvent('initialize', this);
21702         this.pushValue();
21703     },
21704
21705     // private
21706     onDestroy : function(){
21707         
21708         
21709         
21710         if(this.rendered){
21711             
21712             //for (var i =0; i < this.toolbars.length;i++) {
21713             //    // fixme - ask toolbars for heights?
21714             //    this.toolbars[i].onDestroy();
21715            // }
21716             
21717             //this.wrap.dom.innerHTML = '';
21718             //this.wrap.remove();
21719         }
21720     },
21721
21722     // private
21723     onFirstFocus : function(){
21724         
21725         this.assignDocWin();
21726         
21727         
21728         this.activated = true;
21729          
21730     
21731         if(Roo.isGecko){ // prevent silly gecko errors
21732             this.win.focus();
21733             var s = this.win.getSelection();
21734             if(!s.focusNode || s.focusNode.nodeType != 3){
21735                 var r = s.getRangeAt(0);
21736                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21737                 r.collapse(true);
21738                 this.deferFocus();
21739             }
21740             try{
21741                 this.execCmd('useCSS', true);
21742                 this.execCmd('styleWithCSS', false);
21743             }catch(e){}
21744         }
21745         this.owner.fireEvent('activate', this);
21746     },
21747
21748     // private
21749     adjustFont: function(btn){
21750         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21751         //if(Roo.isSafari){ // safari
21752         //    adjust *= 2;
21753        // }
21754         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21755         if(Roo.isSafari){ // safari
21756             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21757             v =  (v < 10) ? 10 : v;
21758             v =  (v > 48) ? 48 : v;
21759             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21760             
21761         }
21762         
21763         
21764         v = Math.max(1, v+adjust);
21765         
21766         this.execCmd('FontSize', v  );
21767     },
21768
21769     onEditorEvent : function(e)
21770     {
21771         this.owner.fireEvent('editorevent', this, e);
21772       //  this.updateToolbar();
21773         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21774     },
21775
21776     insertTag : function(tg)
21777     {
21778         // could be a bit smarter... -> wrap the current selected tRoo..
21779         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21780             
21781             range = this.createRange(this.getSelection());
21782             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21783             wrappingNode.appendChild(range.extractContents());
21784             range.insertNode(wrappingNode);
21785
21786             return;
21787             
21788             
21789             
21790         }
21791         this.execCmd("formatblock",   tg);
21792         
21793     },
21794     
21795     insertText : function(txt)
21796     {
21797         
21798         
21799         var range = this.createRange();
21800         range.deleteContents();
21801                //alert(Sender.getAttribute('label'));
21802                
21803         range.insertNode(this.doc.createTextNode(txt));
21804     } ,
21805     
21806      
21807
21808     /**
21809      * Executes a Midas editor command on the editor document and performs necessary focus and
21810      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21811      * @param {String} cmd The Midas command
21812      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21813      */
21814     relayCmd : function(cmd, value){
21815         this.win.focus();
21816         this.execCmd(cmd, value);
21817         this.owner.fireEvent('editorevent', this);
21818         //this.updateToolbar();
21819         this.owner.deferFocus();
21820     },
21821
21822     /**
21823      * Executes a Midas editor command directly on the editor document.
21824      * For visual commands, you should use {@link #relayCmd} instead.
21825      * <b>This should only be called after the editor is initialized.</b>
21826      * @param {String} cmd The Midas command
21827      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21828      */
21829     execCmd : function(cmd, value){
21830         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21831         this.syncValue();
21832     },
21833  
21834  
21835    
21836     /**
21837      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21838      * to insert tRoo.
21839      * @param {String} text | dom node.. 
21840      */
21841     insertAtCursor : function(text)
21842     {
21843         
21844         if(!this.activated){
21845             return;
21846         }
21847         /*
21848         if(Roo.isIE){
21849             this.win.focus();
21850             var r = this.doc.selection.createRange();
21851             if(r){
21852                 r.collapse(true);
21853                 r.pasteHTML(text);
21854                 this.syncValue();
21855                 this.deferFocus();
21856             
21857             }
21858             return;
21859         }
21860         */
21861         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21862             this.win.focus();
21863             
21864             
21865             // from jquery ui (MIT licenced)
21866             var range, node;
21867             var win = this.win;
21868             
21869             if (win.getSelection && win.getSelection().getRangeAt) {
21870                 range = win.getSelection().getRangeAt(0);
21871                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21872                 range.insertNode(node);
21873             } else if (win.document.selection && win.document.selection.createRange) {
21874                 // no firefox support
21875                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21876                 win.document.selection.createRange().pasteHTML(txt);
21877             } else {
21878                 // no firefox support
21879                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21880                 this.execCmd('InsertHTML', txt);
21881             } 
21882             
21883             this.syncValue();
21884             
21885             this.deferFocus();
21886         }
21887     },
21888  // private
21889     mozKeyPress : function(e){
21890         if(e.ctrlKey){
21891             var c = e.getCharCode(), cmd;
21892           
21893             if(c > 0){
21894                 c = String.fromCharCode(c).toLowerCase();
21895                 switch(c){
21896                     case 'b':
21897                         cmd = 'bold';
21898                         break;
21899                     case 'i':
21900                         cmd = 'italic';
21901                         break;
21902                     
21903                     case 'u':
21904                         cmd = 'underline';
21905                         break;
21906                     
21907                     case 'v':
21908                         this.cleanUpPaste.defer(100, this);
21909                         return;
21910                         
21911                 }
21912                 if(cmd){
21913                     this.win.focus();
21914                     this.execCmd(cmd);
21915                     this.deferFocus();
21916                     e.preventDefault();
21917                 }
21918                 
21919             }
21920         }
21921     },
21922
21923     // private
21924     fixKeys : function(){ // load time branching for fastest keydown performance
21925         if(Roo.isIE){
21926             return function(e){
21927                 var k = e.getKey(), r;
21928                 if(k == e.TAB){
21929                     e.stopEvent();
21930                     r = this.doc.selection.createRange();
21931                     if(r){
21932                         r.collapse(true);
21933                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21934                         this.deferFocus();
21935                     }
21936                     return;
21937                 }
21938                 
21939                 if(k == e.ENTER){
21940                     r = this.doc.selection.createRange();
21941                     if(r){
21942                         var target = r.parentElement();
21943                         if(!target || target.tagName.toLowerCase() != 'li'){
21944                             e.stopEvent();
21945                             r.pasteHTML('<br />');
21946                             r.collapse(false);
21947                             r.select();
21948                         }
21949                     }
21950                 }
21951                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21952                     this.cleanUpPaste.defer(100, this);
21953                     return;
21954                 }
21955                 
21956                 
21957             };
21958         }else if(Roo.isOpera){
21959             return function(e){
21960                 var k = e.getKey();
21961                 if(k == e.TAB){
21962                     e.stopEvent();
21963                     this.win.focus();
21964                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21965                     this.deferFocus();
21966                 }
21967                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21968                     this.cleanUpPaste.defer(100, this);
21969                     return;
21970                 }
21971                 
21972             };
21973         }else if(Roo.isSafari){
21974             return function(e){
21975                 var k = e.getKey();
21976                 
21977                 if(k == e.TAB){
21978                     e.stopEvent();
21979                     this.execCmd('InsertText','\t');
21980                     this.deferFocus();
21981                     return;
21982                 }
21983                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21984                     this.cleanUpPaste.defer(100, this);
21985                     return;
21986                 }
21987                 
21988              };
21989         }
21990     }(),
21991     
21992     getAllAncestors: function()
21993     {
21994         var p = this.getSelectedNode();
21995         var a = [];
21996         if (!p) {
21997             a.push(p); // push blank onto stack..
21998             p = this.getParentElement();
21999         }
22000         
22001         
22002         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22003             a.push(p);
22004             p = p.parentNode;
22005         }
22006         a.push(this.doc.body);
22007         return a;
22008     },
22009     lastSel : false,
22010     lastSelNode : false,
22011     
22012     
22013     getSelection : function() 
22014     {
22015         this.assignDocWin();
22016         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22017     },
22018     
22019     getSelectedNode: function() 
22020     {
22021         // this may only work on Gecko!!!
22022         
22023         // should we cache this!!!!
22024         
22025         
22026         
22027          
22028         var range = this.createRange(this.getSelection()).cloneRange();
22029         
22030         if (Roo.isIE) {
22031             var parent = range.parentElement();
22032             while (true) {
22033                 var testRange = range.duplicate();
22034                 testRange.moveToElementText(parent);
22035                 if (testRange.inRange(range)) {
22036                     break;
22037                 }
22038                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22039                     break;
22040                 }
22041                 parent = parent.parentElement;
22042             }
22043             return parent;
22044         }
22045         
22046         // is ancestor a text element.
22047         var ac =  range.commonAncestorContainer;
22048         if (ac.nodeType == 3) {
22049             ac = ac.parentNode;
22050         }
22051         
22052         var ar = ac.childNodes;
22053          
22054         var nodes = [];
22055         var other_nodes = [];
22056         var has_other_nodes = false;
22057         for (var i=0;i<ar.length;i++) {
22058             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22059                 continue;
22060             }
22061             // fullly contained node.
22062             
22063             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22064                 nodes.push(ar[i]);
22065                 continue;
22066             }
22067             
22068             // probably selected..
22069             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22070                 other_nodes.push(ar[i]);
22071                 continue;
22072             }
22073             // outer..
22074             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22075                 continue;
22076             }
22077             
22078             
22079             has_other_nodes = true;
22080         }
22081         if (!nodes.length && other_nodes.length) {
22082             nodes= other_nodes;
22083         }
22084         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22085             return false;
22086         }
22087         
22088         return nodes[0];
22089     },
22090     createRange: function(sel)
22091     {
22092         // this has strange effects when using with 
22093         // top toolbar - not sure if it's a great idea.
22094         //this.editor.contentWindow.focus();
22095         if (typeof sel != "undefined") {
22096             try {
22097                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22098             } catch(e) {
22099                 return this.doc.createRange();
22100             }
22101         } else {
22102             return this.doc.createRange();
22103         }
22104     },
22105     getParentElement: function()
22106     {
22107         
22108         this.assignDocWin();
22109         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22110         
22111         var range = this.createRange(sel);
22112          
22113         try {
22114             var p = range.commonAncestorContainer;
22115             while (p.nodeType == 3) { // text node
22116                 p = p.parentNode;
22117             }
22118             return p;
22119         } catch (e) {
22120             return null;
22121         }
22122     
22123     },
22124     /***
22125      *
22126      * Range intersection.. the hard stuff...
22127      *  '-1' = before
22128      *  '0' = hits..
22129      *  '1' = after.
22130      *         [ -- selected range --- ]
22131      *   [fail]                        [fail]
22132      *
22133      *    basically..
22134      *      if end is before start or  hits it. fail.
22135      *      if start is after end or hits it fail.
22136      *
22137      *   if either hits (but other is outside. - then it's not 
22138      *   
22139      *    
22140      **/
22141     
22142     
22143     // @see http://www.thismuchiknow.co.uk/?p=64.
22144     rangeIntersectsNode : function(range, node)
22145     {
22146         var nodeRange = node.ownerDocument.createRange();
22147         try {
22148             nodeRange.selectNode(node);
22149         } catch (e) {
22150             nodeRange.selectNodeContents(node);
22151         }
22152     
22153         var rangeStartRange = range.cloneRange();
22154         rangeStartRange.collapse(true);
22155     
22156         var rangeEndRange = range.cloneRange();
22157         rangeEndRange.collapse(false);
22158     
22159         var nodeStartRange = nodeRange.cloneRange();
22160         nodeStartRange.collapse(true);
22161     
22162         var nodeEndRange = nodeRange.cloneRange();
22163         nodeEndRange.collapse(false);
22164     
22165         return rangeStartRange.compareBoundaryPoints(
22166                  Range.START_TO_START, nodeEndRange) == -1 &&
22167                rangeEndRange.compareBoundaryPoints(
22168                  Range.START_TO_START, nodeStartRange) == 1;
22169         
22170          
22171     },
22172     rangeCompareNode : function(range, node)
22173     {
22174         var nodeRange = node.ownerDocument.createRange();
22175         try {
22176             nodeRange.selectNode(node);
22177         } catch (e) {
22178             nodeRange.selectNodeContents(node);
22179         }
22180         
22181         
22182         range.collapse(true);
22183     
22184         nodeRange.collapse(true);
22185      
22186         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22187         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22188          
22189         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22190         
22191         var nodeIsBefore   =  ss == 1;
22192         var nodeIsAfter    = ee == -1;
22193         
22194         if (nodeIsBefore && nodeIsAfter) {
22195             return 0; // outer
22196         }
22197         if (!nodeIsBefore && nodeIsAfter) {
22198             return 1; //right trailed.
22199         }
22200         
22201         if (nodeIsBefore && !nodeIsAfter) {
22202             return 2;  // left trailed.
22203         }
22204         // fully contined.
22205         return 3;
22206     },
22207
22208     // private? - in a new class?
22209     cleanUpPaste :  function()
22210     {
22211         // cleans up the whole document..
22212         Roo.log('cleanuppaste');
22213         
22214         this.cleanUpChildren(this.doc.body);
22215         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22216         if (clean != this.doc.body.innerHTML) {
22217             this.doc.body.innerHTML = clean;
22218         }
22219         
22220     },
22221     
22222     cleanWordChars : function(input) {// change the chars to hex code
22223         var he = Roo.HtmlEditorCore;
22224         
22225         var output = input;
22226         Roo.each(he.swapCodes, function(sw) { 
22227             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22228             
22229             output = output.replace(swapper, sw[1]);
22230         });
22231         
22232         return output;
22233     },
22234     
22235     
22236     cleanUpChildren : function (n)
22237     {
22238         if (!n.childNodes.length) {
22239             return;
22240         }
22241         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22242            this.cleanUpChild(n.childNodes[i]);
22243         }
22244     },
22245     
22246     
22247         
22248     
22249     cleanUpChild : function (node)
22250     {
22251         var ed = this;
22252         //console.log(node);
22253         if (node.nodeName == "#text") {
22254             // clean up silly Windows -- stuff?
22255             return; 
22256         }
22257         if (node.nodeName == "#comment") {
22258             node.parentNode.removeChild(node);
22259             // clean up silly Windows -- stuff?
22260             return; 
22261         }
22262         var lcname = node.tagName.toLowerCase();
22263         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22264         // whitelist of tags..
22265         
22266         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22267             // remove node.
22268             node.parentNode.removeChild(node);
22269             return;
22270             
22271         }
22272         
22273         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22274         
22275         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22276         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22277         
22278         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22279         //    remove_keep_children = true;
22280         //}
22281         
22282         if (remove_keep_children) {
22283             this.cleanUpChildren(node);
22284             // inserts everything just before this node...
22285             while (node.childNodes.length) {
22286                 var cn = node.childNodes[0];
22287                 node.removeChild(cn);
22288                 node.parentNode.insertBefore(cn, node);
22289             }
22290             node.parentNode.removeChild(node);
22291             return;
22292         }
22293         
22294         if (!node.attributes || !node.attributes.length) {
22295             this.cleanUpChildren(node);
22296             return;
22297         }
22298         
22299         function cleanAttr(n,v)
22300         {
22301             
22302             if (v.match(/^\./) || v.match(/^\//)) {
22303                 return;
22304             }
22305             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22306                 return;
22307             }
22308             if (v.match(/^#/)) {
22309                 return;
22310             }
22311 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22312             node.removeAttribute(n);
22313             
22314         }
22315         
22316         var cwhite = this.cwhite;
22317         var cblack = this.cblack;
22318             
22319         function cleanStyle(n,v)
22320         {
22321             if (v.match(/expression/)) { //XSS?? should we even bother..
22322                 node.removeAttribute(n);
22323                 return;
22324             }
22325             
22326             var parts = v.split(/;/);
22327             var clean = [];
22328             
22329             Roo.each(parts, function(p) {
22330                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22331                 if (!p.length) {
22332                     return true;
22333                 }
22334                 var l = p.split(':').shift().replace(/\s+/g,'');
22335                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22336                 
22337                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22338 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22339                     //node.removeAttribute(n);
22340                     return true;
22341                 }
22342                 //Roo.log()
22343                 // only allow 'c whitelisted system attributes'
22344                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22345 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22346                     //node.removeAttribute(n);
22347                     return true;
22348                 }
22349                 
22350                 
22351                  
22352                 
22353                 clean.push(p);
22354                 return true;
22355             });
22356             if (clean.length) { 
22357                 node.setAttribute(n, clean.join(';'));
22358             } else {
22359                 node.removeAttribute(n);
22360             }
22361             
22362         }
22363         
22364         
22365         for (var i = node.attributes.length-1; i > -1 ; i--) {
22366             var a = node.attributes[i];
22367             //console.log(a);
22368             
22369             if (a.name.toLowerCase().substr(0,2)=='on')  {
22370                 node.removeAttribute(a.name);
22371                 continue;
22372             }
22373             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22374                 node.removeAttribute(a.name);
22375                 continue;
22376             }
22377             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22378                 cleanAttr(a.name,a.value); // fixme..
22379                 continue;
22380             }
22381             if (a.name == 'style') {
22382                 cleanStyle(a.name,a.value);
22383                 continue;
22384             }
22385             /// clean up MS crap..
22386             // tecnically this should be a list of valid class'es..
22387             
22388             
22389             if (a.name == 'class') {
22390                 if (a.value.match(/^Mso/)) {
22391                     node.className = '';
22392                 }
22393                 
22394                 if (a.value.match(/^body$/)) {
22395                     node.className = '';
22396                 }
22397                 continue;
22398             }
22399             
22400             // style cleanup!?
22401             // class cleanup?
22402             
22403         }
22404         
22405         
22406         this.cleanUpChildren(node);
22407         
22408         
22409     },
22410     
22411     /**
22412      * Clean up MS wordisms...
22413      */
22414     cleanWord : function(node)
22415     {
22416         
22417         
22418         if (!node) {
22419             this.cleanWord(this.doc.body);
22420             return;
22421         }
22422         if (node.nodeName == "#text") {
22423             // clean up silly Windows -- stuff?
22424             return; 
22425         }
22426         if (node.nodeName == "#comment") {
22427             node.parentNode.removeChild(node);
22428             // clean up silly Windows -- stuff?
22429             return; 
22430         }
22431         
22432         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22433             node.parentNode.removeChild(node);
22434             return;
22435         }
22436         
22437         // remove - but keep children..
22438         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22439             while (node.childNodes.length) {
22440                 var cn = node.childNodes[0];
22441                 node.removeChild(cn);
22442                 node.parentNode.insertBefore(cn, node);
22443             }
22444             node.parentNode.removeChild(node);
22445             this.iterateChildren(node, this.cleanWord);
22446             return;
22447         }
22448         // clean styles
22449         if (node.className.length) {
22450             
22451             var cn = node.className.split(/\W+/);
22452             var cna = [];
22453             Roo.each(cn, function(cls) {
22454                 if (cls.match(/Mso[a-zA-Z]+/)) {
22455                     return;
22456                 }
22457                 cna.push(cls);
22458             });
22459             node.className = cna.length ? cna.join(' ') : '';
22460             if (!cna.length) {
22461                 node.removeAttribute("class");
22462             }
22463         }
22464         
22465         if (node.hasAttribute("lang")) {
22466             node.removeAttribute("lang");
22467         }
22468         
22469         if (node.hasAttribute("style")) {
22470             
22471             var styles = node.getAttribute("style").split(";");
22472             var nstyle = [];
22473             Roo.each(styles, function(s) {
22474                 if (!s.match(/:/)) {
22475                     return;
22476                 }
22477                 var kv = s.split(":");
22478                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22479                     return;
22480                 }
22481                 // what ever is left... we allow.
22482                 nstyle.push(s);
22483             });
22484             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22485             if (!nstyle.length) {
22486                 node.removeAttribute('style');
22487             }
22488         }
22489         this.iterateChildren(node, this.cleanWord);
22490         
22491         
22492         
22493     },
22494     /**
22495      * iterateChildren of a Node, calling fn each time, using this as the scole..
22496      * @param {DomNode} node node to iterate children of.
22497      * @param {Function} fn method of this class to call on each item.
22498      */
22499     iterateChildren : function(node, fn)
22500     {
22501         if (!node.childNodes.length) {
22502                 return;
22503         }
22504         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22505            fn.call(this, node.childNodes[i])
22506         }
22507     },
22508     
22509     
22510     /**
22511      * cleanTableWidths.
22512      *
22513      * Quite often pasting from word etc.. results in tables with column and widths.
22514      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22515      *
22516      */
22517     cleanTableWidths : function(node)
22518     {
22519          
22520          
22521         if (!node) {
22522             this.cleanTableWidths(this.doc.body);
22523             return;
22524         }
22525         
22526         // ignore list...
22527         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22528             return; 
22529         }
22530         Roo.log(node.tagName);
22531         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22532             this.iterateChildren(node, this.cleanTableWidths);
22533             return;
22534         }
22535         if (node.hasAttribute('width')) {
22536             node.removeAttribute('width');
22537         }
22538         
22539          
22540         if (node.hasAttribute("style")) {
22541             // pretty basic...
22542             
22543             var styles = node.getAttribute("style").split(";");
22544             var nstyle = [];
22545             Roo.each(styles, function(s) {
22546                 if (!s.match(/:/)) {
22547                     return;
22548                 }
22549                 var kv = s.split(":");
22550                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22551                     return;
22552                 }
22553                 // what ever is left... we allow.
22554                 nstyle.push(s);
22555             });
22556             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22557             if (!nstyle.length) {
22558                 node.removeAttribute('style');
22559             }
22560         }
22561         
22562         this.iterateChildren(node, this.cleanTableWidths);
22563         
22564         
22565     },
22566     
22567     
22568     
22569     
22570     domToHTML : function(currentElement, depth, nopadtext) {
22571         
22572         depth = depth || 0;
22573         nopadtext = nopadtext || false;
22574     
22575         if (!currentElement) {
22576             return this.domToHTML(this.doc.body);
22577         }
22578         
22579         //Roo.log(currentElement);
22580         var j;
22581         var allText = false;
22582         var nodeName = currentElement.nodeName;
22583         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22584         
22585         if  (nodeName == '#text') {
22586             
22587             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22588         }
22589         
22590         
22591         var ret = '';
22592         if (nodeName != 'BODY') {
22593              
22594             var i = 0;
22595             // Prints the node tagName, such as <A>, <IMG>, etc
22596             if (tagName) {
22597                 var attr = [];
22598                 for(i = 0; i < currentElement.attributes.length;i++) {
22599                     // quoting?
22600                     var aname = currentElement.attributes.item(i).name;
22601                     if (!currentElement.attributes.item(i).value.length) {
22602                         continue;
22603                     }
22604                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22605                 }
22606                 
22607                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22608             } 
22609             else {
22610                 
22611                 // eack
22612             }
22613         } else {
22614             tagName = false;
22615         }
22616         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22617             return ret;
22618         }
22619         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22620             nopadtext = true;
22621         }
22622         
22623         
22624         // Traverse the tree
22625         i = 0;
22626         var currentElementChild = currentElement.childNodes.item(i);
22627         var allText = true;
22628         var innerHTML  = '';
22629         lastnode = '';
22630         while (currentElementChild) {
22631             // Formatting code (indent the tree so it looks nice on the screen)
22632             var nopad = nopadtext;
22633             if (lastnode == 'SPAN') {
22634                 nopad  = true;
22635             }
22636             // text
22637             if  (currentElementChild.nodeName == '#text') {
22638                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22639                 toadd = nopadtext ? toadd : toadd.trim();
22640                 if (!nopad && toadd.length > 80) {
22641                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22642                 }
22643                 innerHTML  += toadd;
22644                 
22645                 i++;
22646                 currentElementChild = currentElement.childNodes.item(i);
22647                 lastNode = '';
22648                 continue;
22649             }
22650             allText = false;
22651             
22652             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22653                 
22654             // Recursively traverse the tree structure of the child node
22655             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22656             lastnode = currentElementChild.nodeName;
22657             i++;
22658             currentElementChild=currentElement.childNodes.item(i);
22659         }
22660         
22661         ret += innerHTML;
22662         
22663         if (!allText) {
22664                 // The remaining code is mostly for formatting the tree
22665             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22666         }
22667         
22668         
22669         if (tagName) {
22670             ret+= "</"+tagName+">";
22671         }
22672         return ret;
22673         
22674     },
22675         
22676     applyBlacklists : function()
22677     {
22678         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22679         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22680         
22681         this.white = [];
22682         this.black = [];
22683         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22684             if (b.indexOf(tag) > -1) {
22685                 return;
22686             }
22687             this.white.push(tag);
22688             
22689         }, this);
22690         
22691         Roo.each(w, function(tag) {
22692             if (b.indexOf(tag) > -1) {
22693                 return;
22694             }
22695             if (this.white.indexOf(tag) > -1) {
22696                 return;
22697             }
22698             this.white.push(tag);
22699             
22700         }, this);
22701         
22702         
22703         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22704             if (w.indexOf(tag) > -1) {
22705                 return;
22706             }
22707             this.black.push(tag);
22708             
22709         }, this);
22710         
22711         Roo.each(b, function(tag) {
22712             if (w.indexOf(tag) > -1) {
22713                 return;
22714             }
22715             if (this.black.indexOf(tag) > -1) {
22716                 return;
22717             }
22718             this.black.push(tag);
22719             
22720         }, this);
22721         
22722         
22723         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22724         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22725         
22726         this.cwhite = [];
22727         this.cblack = [];
22728         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22729             if (b.indexOf(tag) > -1) {
22730                 return;
22731             }
22732             this.cwhite.push(tag);
22733             
22734         }, this);
22735         
22736         Roo.each(w, function(tag) {
22737             if (b.indexOf(tag) > -1) {
22738                 return;
22739             }
22740             if (this.cwhite.indexOf(tag) > -1) {
22741                 return;
22742             }
22743             this.cwhite.push(tag);
22744             
22745         }, this);
22746         
22747         
22748         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22749             if (w.indexOf(tag) > -1) {
22750                 return;
22751             }
22752             this.cblack.push(tag);
22753             
22754         }, this);
22755         
22756         Roo.each(b, function(tag) {
22757             if (w.indexOf(tag) > -1) {
22758                 return;
22759             }
22760             if (this.cblack.indexOf(tag) > -1) {
22761                 return;
22762             }
22763             this.cblack.push(tag);
22764             
22765         }, this);
22766     },
22767     
22768     setStylesheets : function(stylesheets)
22769     {
22770         if(typeof(stylesheets) == 'string'){
22771             Roo.get(this.iframe.contentDocument.head).createChild({
22772                 tag : 'link',
22773                 rel : 'stylesheet',
22774                 type : 'text/css',
22775                 href : stylesheets
22776             });
22777             
22778             return;
22779         }
22780         var _this = this;
22781      
22782         Roo.each(stylesheets, function(s) {
22783             if(!s.length){
22784                 return;
22785             }
22786             
22787             Roo.get(_this.iframe.contentDocument.head).createChild({
22788                 tag : 'link',
22789                 rel : 'stylesheet',
22790                 type : 'text/css',
22791                 href : s
22792             });
22793         });
22794
22795         
22796     },
22797     
22798     removeStylesheets : function()
22799     {
22800         var _this = this;
22801         
22802         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22803             s.remove();
22804         });
22805     },
22806     
22807     setStyle : function(style)
22808     {
22809         Roo.get(this.iframe.contentDocument.head).createChild({
22810             tag : 'style',
22811             type : 'text/css',
22812             html : style
22813         });
22814
22815         return;
22816     }
22817     
22818     // hide stuff that is not compatible
22819     /**
22820      * @event blur
22821      * @hide
22822      */
22823     /**
22824      * @event change
22825      * @hide
22826      */
22827     /**
22828      * @event focus
22829      * @hide
22830      */
22831     /**
22832      * @event specialkey
22833      * @hide
22834      */
22835     /**
22836      * @cfg {String} fieldClass @hide
22837      */
22838     /**
22839      * @cfg {String} focusClass @hide
22840      */
22841     /**
22842      * @cfg {String} autoCreate @hide
22843      */
22844     /**
22845      * @cfg {String} inputType @hide
22846      */
22847     /**
22848      * @cfg {String} invalidClass @hide
22849      */
22850     /**
22851      * @cfg {String} invalidText @hide
22852      */
22853     /**
22854      * @cfg {String} msgFx @hide
22855      */
22856     /**
22857      * @cfg {String} validateOnBlur @hide
22858      */
22859 });
22860
22861 Roo.HtmlEditorCore.white = [
22862         'area', 'br', 'img', 'input', 'hr', 'wbr',
22863         
22864        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22865        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22866        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22867        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22868        'table',   'ul',         'xmp', 
22869        
22870        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22871       'thead',   'tr', 
22872      
22873       'dir', 'menu', 'ol', 'ul', 'dl',
22874        
22875       'embed',  'object'
22876 ];
22877
22878
22879 Roo.HtmlEditorCore.black = [
22880     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22881         'applet', // 
22882         'base',   'basefont', 'bgsound', 'blink',  'body', 
22883         'frame',  'frameset', 'head',    'html',   'ilayer', 
22884         'iframe', 'layer',  'link',     'meta',    'object',   
22885         'script', 'style' ,'title',  'xml' // clean later..
22886 ];
22887 Roo.HtmlEditorCore.clean = [
22888     'script', 'style', 'title', 'xml'
22889 ];
22890 Roo.HtmlEditorCore.remove = [
22891     'font'
22892 ];
22893 // attributes..
22894
22895 Roo.HtmlEditorCore.ablack = [
22896     'on'
22897 ];
22898     
22899 Roo.HtmlEditorCore.aclean = [ 
22900     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22901 ];
22902
22903 // protocols..
22904 Roo.HtmlEditorCore.pwhite= [
22905         'http',  'https',  'mailto'
22906 ];
22907
22908 // white listed style attributes.
22909 Roo.HtmlEditorCore.cwhite= [
22910       //  'text-align', /// default is to allow most things..
22911       
22912          
22913 //        'font-size'//??
22914 ];
22915
22916 // black listed style attributes.
22917 Roo.HtmlEditorCore.cblack= [
22918       //  'font-size' -- this can be set by the project 
22919 ];
22920
22921
22922 Roo.HtmlEditorCore.swapCodes   =[ 
22923     [    8211, "--" ], 
22924     [    8212, "--" ], 
22925     [    8216,  "'" ],  
22926     [    8217, "'" ],  
22927     [    8220, '"' ],  
22928     [    8221, '"' ],  
22929     [    8226, "*" ],  
22930     [    8230, "..." ]
22931 ]; 
22932
22933     /*
22934  * - LGPL
22935  *
22936  * HtmlEditor
22937  * 
22938  */
22939
22940 /**
22941  * @class Roo.bootstrap.HtmlEditor
22942  * @extends Roo.bootstrap.TextArea
22943  * Bootstrap HtmlEditor class
22944
22945  * @constructor
22946  * Create a new HtmlEditor
22947  * @param {Object} config The config object
22948  */
22949
22950 Roo.bootstrap.HtmlEditor = function(config){
22951     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22952     if (!this.toolbars) {
22953         this.toolbars = [];
22954     }
22955     
22956     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22957     this.addEvents({
22958             /**
22959              * @event initialize
22960              * Fires when the editor is fully initialized (including the iframe)
22961              * @param {HtmlEditor} this
22962              */
22963             initialize: true,
22964             /**
22965              * @event activate
22966              * Fires when the editor is first receives the focus. Any insertion must wait
22967              * until after this event.
22968              * @param {HtmlEditor} this
22969              */
22970             activate: true,
22971              /**
22972              * @event beforesync
22973              * Fires before the textarea is updated with content from the editor iframe. Return false
22974              * to cancel the sync.
22975              * @param {HtmlEditor} this
22976              * @param {String} html
22977              */
22978             beforesync: true,
22979              /**
22980              * @event beforepush
22981              * Fires before the iframe editor is updated with content from the textarea. Return false
22982              * to cancel the push.
22983              * @param {HtmlEditor} this
22984              * @param {String} html
22985              */
22986             beforepush: true,
22987              /**
22988              * @event sync
22989              * Fires when the textarea is updated with content from the editor iframe.
22990              * @param {HtmlEditor} this
22991              * @param {String} html
22992              */
22993             sync: true,
22994              /**
22995              * @event push
22996              * Fires when the iframe editor is updated with content from the textarea.
22997              * @param {HtmlEditor} this
22998              * @param {String} html
22999              */
23000             push: true,
23001              /**
23002              * @event editmodechange
23003              * Fires when the editor switches edit modes
23004              * @param {HtmlEditor} this
23005              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23006              */
23007             editmodechange: true,
23008             /**
23009              * @event editorevent
23010              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23011              * @param {HtmlEditor} this
23012              */
23013             editorevent: true,
23014             /**
23015              * @event firstfocus
23016              * Fires when on first focus - needed by toolbars..
23017              * @param {HtmlEditor} this
23018              */
23019             firstfocus: true,
23020             /**
23021              * @event autosave
23022              * Auto save the htmlEditor value as a file into Events
23023              * @param {HtmlEditor} this
23024              */
23025             autosave: true,
23026             /**
23027              * @event savedpreview
23028              * preview the saved version of htmlEditor
23029              * @param {HtmlEditor} this
23030              */
23031             savedpreview: true
23032         });
23033 };
23034
23035
23036 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23037     
23038     
23039       /**
23040      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23041      */
23042     toolbars : false,
23043     
23044      /**
23045     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23046     */
23047     btns : [],
23048    
23049      /**
23050      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23051      *                        Roo.resizable.
23052      */
23053     resizable : false,
23054      /**
23055      * @cfg {Number} height (in pixels)
23056      */   
23057     height: 300,
23058    /**
23059      * @cfg {Number} width (in pixels)
23060      */   
23061     width: false,
23062     
23063     /**
23064      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23065      * 
23066      */
23067     stylesheets: false,
23068     
23069     // id of frame..
23070     frameId: false,
23071     
23072     // private properties
23073     validationEvent : false,
23074     deferHeight: true,
23075     initialized : false,
23076     activated : false,
23077     
23078     onFocus : Roo.emptyFn,
23079     iframePad:3,
23080     hideMode:'offsets',
23081     
23082     tbContainer : false,
23083     
23084     bodyCls : '',
23085     
23086     toolbarContainer :function() {
23087         return this.wrap.select('.x-html-editor-tb',true).first();
23088     },
23089
23090     /**
23091      * Protected method that will not generally be called directly. It
23092      * is called when the editor creates its toolbar. Override this method if you need to
23093      * add custom toolbar buttons.
23094      * @param {HtmlEditor} editor
23095      */
23096     createToolbar : function(){
23097         Roo.log('renewing');
23098         Roo.log("create toolbars");
23099         
23100         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23101         this.toolbars[0].render(this.toolbarContainer());
23102         
23103         return;
23104         
23105 //        if (!editor.toolbars || !editor.toolbars.length) {
23106 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23107 //        }
23108 //        
23109 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23110 //            editor.toolbars[i] = Roo.factory(
23111 //                    typeof(editor.toolbars[i]) == 'string' ?
23112 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23113 //                Roo.bootstrap.HtmlEditor);
23114 //            editor.toolbars[i].init(editor);
23115 //        }
23116     },
23117
23118      
23119     // private
23120     onRender : function(ct, position)
23121     {
23122        // Roo.log("Call onRender: " + this.xtype);
23123         var _t = this;
23124         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23125       
23126         this.wrap = this.inputEl().wrap({
23127             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23128         });
23129         
23130         this.editorcore.onRender(ct, position);
23131          
23132         if (this.resizable) {
23133             this.resizeEl = new Roo.Resizable(this.wrap, {
23134                 pinned : true,
23135                 wrap: true,
23136                 dynamic : true,
23137                 minHeight : this.height,
23138                 height: this.height,
23139                 handles : this.resizable,
23140                 width: this.width,
23141                 listeners : {
23142                     resize : function(r, w, h) {
23143                         _t.onResize(w,h); // -something
23144                     }
23145                 }
23146             });
23147             
23148         }
23149         this.createToolbar(this);
23150        
23151         
23152         if(!this.width && this.resizable){
23153             this.setSize(this.wrap.getSize());
23154         }
23155         if (this.resizeEl) {
23156             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23157             // should trigger onReize..
23158         }
23159         
23160     },
23161
23162     // private
23163     onResize : function(w, h)
23164     {
23165         Roo.log('resize: ' +w + ',' + h );
23166         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23167         var ew = false;
23168         var eh = false;
23169         
23170         if(this.inputEl() ){
23171             if(typeof w == 'number'){
23172                 var aw = w - this.wrap.getFrameWidth('lr');
23173                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23174                 ew = aw;
23175             }
23176             if(typeof h == 'number'){
23177                  var tbh = -11;  // fixme it needs to tool bar size!
23178                 for (var i =0; i < this.toolbars.length;i++) {
23179                     // fixme - ask toolbars for heights?
23180                     tbh += this.toolbars[i].el.getHeight();
23181                     //if (this.toolbars[i].footer) {
23182                     //    tbh += this.toolbars[i].footer.el.getHeight();
23183                     //}
23184                 }
23185               
23186                 
23187                 
23188                 
23189                 
23190                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23191                 ah -= 5; // knock a few pixes off for look..
23192                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23193                 var eh = ah;
23194             }
23195         }
23196         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23197         this.editorcore.onResize(ew,eh);
23198         
23199     },
23200
23201     /**
23202      * Toggles the editor between standard and source edit mode.
23203      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23204      */
23205     toggleSourceEdit : function(sourceEditMode)
23206     {
23207         this.editorcore.toggleSourceEdit(sourceEditMode);
23208         
23209         if(this.editorcore.sourceEditMode){
23210             Roo.log('editor - showing textarea');
23211             
23212 //            Roo.log('in');
23213 //            Roo.log(this.syncValue());
23214             this.syncValue();
23215             this.inputEl().removeClass(['hide', 'x-hidden']);
23216             this.inputEl().dom.removeAttribute('tabIndex');
23217             this.inputEl().focus();
23218         }else{
23219             Roo.log('editor - hiding textarea');
23220 //            Roo.log('out')
23221 //            Roo.log(this.pushValue()); 
23222             this.pushValue();
23223             
23224             this.inputEl().addClass(['hide', 'x-hidden']);
23225             this.inputEl().dom.setAttribute('tabIndex', -1);
23226             //this.deferFocus();
23227         }
23228          
23229         if(this.resizable){
23230             this.setSize(this.wrap.getSize());
23231         }
23232         
23233         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23234     },
23235  
23236     // private (for BoxComponent)
23237     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23238
23239     // private (for BoxComponent)
23240     getResizeEl : function(){
23241         return this.wrap;
23242     },
23243
23244     // private (for BoxComponent)
23245     getPositionEl : function(){
23246         return this.wrap;
23247     },
23248
23249     // private
23250     initEvents : function(){
23251         this.originalValue = this.getValue();
23252     },
23253
23254 //    /**
23255 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23256 //     * @method
23257 //     */
23258 //    markInvalid : Roo.emptyFn,
23259 //    /**
23260 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23261 //     * @method
23262 //     */
23263 //    clearInvalid : Roo.emptyFn,
23264
23265     setValue : function(v){
23266         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23267         this.editorcore.pushValue();
23268     },
23269
23270      
23271     // private
23272     deferFocus : function(){
23273         this.focus.defer(10, this);
23274     },
23275
23276     // doc'ed in Field
23277     focus : function(){
23278         this.editorcore.focus();
23279         
23280     },
23281       
23282
23283     // private
23284     onDestroy : function(){
23285         
23286         
23287         
23288         if(this.rendered){
23289             
23290             for (var i =0; i < this.toolbars.length;i++) {
23291                 // fixme - ask toolbars for heights?
23292                 this.toolbars[i].onDestroy();
23293             }
23294             
23295             this.wrap.dom.innerHTML = '';
23296             this.wrap.remove();
23297         }
23298     },
23299
23300     // private
23301     onFirstFocus : function(){
23302         //Roo.log("onFirstFocus");
23303         this.editorcore.onFirstFocus();
23304          for (var i =0; i < this.toolbars.length;i++) {
23305             this.toolbars[i].onFirstFocus();
23306         }
23307         
23308     },
23309     
23310     // private
23311     syncValue : function()
23312     {   
23313         this.editorcore.syncValue();
23314     },
23315     
23316     pushValue : function()
23317     {   
23318         this.editorcore.pushValue();
23319     }
23320      
23321     
23322     // hide stuff that is not compatible
23323     /**
23324      * @event blur
23325      * @hide
23326      */
23327     /**
23328      * @event change
23329      * @hide
23330      */
23331     /**
23332      * @event focus
23333      * @hide
23334      */
23335     /**
23336      * @event specialkey
23337      * @hide
23338      */
23339     /**
23340      * @cfg {String} fieldClass @hide
23341      */
23342     /**
23343      * @cfg {String} focusClass @hide
23344      */
23345     /**
23346      * @cfg {String} autoCreate @hide
23347      */
23348     /**
23349      * @cfg {String} inputType @hide
23350      */
23351     /**
23352      * @cfg {String} invalidClass @hide
23353      */
23354     /**
23355      * @cfg {String} invalidText @hide
23356      */
23357     /**
23358      * @cfg {String} msgFx @hide
23359      */
23360     /**
23361      * @cfg {String} validateOnBlur @hide
23362      */
23363 });
23364  
23365     
23366    
23367    
23368    
23369       
23370 Roo.namespace('Roo.bootstrap.htmleditor');
23371 /**
23372  * @class Roo.bootstrap.HtmlEditorToolbar1
23373  * Basic Toolbar
23374  * 
23375  * Usage:
23376  *
23377  new Roo.bootstrap.HtmlEditor({
23378     ....
23379     toolbars : [
23380         new Roo.bootstrap.HtmlEditorToolbar1({
23381             disable : { fonts: 1 , format: 1, ..., ... , ...],
23382             btns : [ .... ]
23383         })
23384     }
23385      
23386  * 
23387  * @cfg {Object} disable List of elements to disable..
23388  * @cfg {Array} btns List of additional buttons.
23389  * 
23390  * 
23391  * NEEDS Extra CSS? 
23392  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23393  */
23394  
23395 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23396 {
23397     
23398     Roo.apply(this, config);
23399     
23400     // default disabled, based on 'good practice'..
23401     this.disable = this.disable || {};
23402     Roo.applyIf(this.disable, {
23403         fontSize : true,
23404         colors : true,
23405         specialElements : true
23406     });
23407     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23408     
23409     this.editor = config.editor;
23410     this.editorcore = config.editor.editorcore;
23411     
23412     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23413     
23414     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23415     // dont call parent... till later.
23416 }
23417 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23418      
23419     bar : true,
23420     
23421     editor : false,
23422     editorcore : false,
23423     
23424     
23425     formats : [
23426         "p" ,  
23427         "h1","h2","h3","h4","h5","h6", 
23428         "pre", "code", 
23429         "abbr", "acronym", "address", "cite", "samp", "var",
23430         'div','span'
23431     ],
23432     
23433     onRender : function(ct, position)
23434     {
23435        // Roo.log("Call onRender: " + this.xtype);
23436         
23437        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23438        Roo.log(this.el);
23439        this.el.dom.style.marginBottom = '0';
23440        var _this = this;
23441        var editorcore = this.editorcore;
23442        var editor= this.editor;
23443        
23444        var children = [];
23445        var btn = function(id,cmd , toggle, handler, html){
23446        
23447             var  event = toggle ? 'toggle' : 'click';
23448        
23449             var a = {
23450                 size : 'sm',
23451                 xtype: 'Button',
23452                 xns: Roo.bootstrap,
23453                 glyphicon : id,
23454                 cmd : id || cmd,
23455                 enableToggle:toggle !== false,
23456                 html : html || '',
23457                 pressed : toggle ? false : null,
23458                 listeners : {}
23459             };
23460             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23461                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23462             };
23463             children.push(a);
23464             return a;
23465        }
23466        
23467     //    var cb_box = function...
23468         
23469         var style = {
23470                 xtype: 'Button',
23471                 size : 'sm',
23472                 xns: Roo.bootstrap,
23473                 glyphicon : 'font',
23474                 //html : 'submit'
23475                 menu : {
23476                     xtype: 'Menu',
23477                     xns: Roo.bootstrap,
23478                     items:  []
23479                 }
23480         };
23481         Roo.each(this.formats, function(f) {
23482             style.menu.items.push({
23483                 xtype :'MenuItem',
23484                 xns: Roo.bootstrap,
23485                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23486                 tagname : f,
23487                 listeners : {
23488                     click : function()
23489                     {
23490                         editorcore.insertTag(this.tagname);
23491                         editor.focus();
23492                     }
23493                 }
23494                 
23495             });
23496         });
23497         children.push(style);   
23498         
23499         btn('bold',false,true);
23500         btn('italic',false,true);
23501         btn('align-left', 'justifyleft',true);
23502         btn('align-center', 'justifycenter',true);
23503         btn('align-right' , 'justifyright',true);
23504         btn('link', false, false, function(btn) {
23505             //Roo.log("create link?");
23506             var url = prompt(this.createLinkText, this.defaultLinkValue);
23507             if(url && url != 'http:/'+'/'){
23508                 this.editorcore.relayCmd('createlink', url);
23509             }
23510         }),
23511         btn('list','insertunorderedlist',true);
23512         btn('pencil', false,true, function(btn){
23513                 Roo.log(this);
23514                 this.toggleSourceEdit(btn.pressed);
23515         });
23516         
23517         if (this.editor.btns.length > 0) {
23518             for (var i = 0; i<this.editor.btns.length; i++) {
23519                 children.push(this.editor.btns[i]);
23520             }
23521         }
23522         
23523         /*
23524         var cog = {
23525                 xtype: 'Button',
23526                 size : 'sm',
23527                 xns: Roo.bootstrap,
23528                 glyphicon : 'cog',
23529                 //html : 'submit'
23530                 menu : {
23531                     xtype: 'Menu',
23532                     xns: Roo.bootstrap,
23533                     items:  []
23534                 }
23535         };
23536         
23537         cog.menu.items.push({
23538             xtype :'MenuItem',
23539             xns: Roo.bootstrap,
23540             html : Clean styles,
23541             tagname : f,
23542             listeners : {
23543                 click : function()
23544                 {
23545                     editorcore.insertTag(this.tagname);
23546                     editor.focus();
23547                 }
23548             }
23549             
23550         });
23551        */
23552         
23553          
23554        this.xtype = 'NavSimplebar';
23555         
23556         for(var i=0;i< children.length;i++) {
23557             
23558             this.buttons.add(this.addxtypeChild(children[i]));
23559             
23560         }
23561         
23562         editor.on('editorevent', this.updateToolbar, this);
23563     },
23564     onBtnClick : function(id)
23565     {
23566        this.editorcore.relayCmd(id);
23567        this.editorcore.focus();
23568     },
23569     
23570     /**
23571      * Protected method that will not generally be called directly. It triggers
23572      * a toolbar update by reading the markup state of the current selection in the editor.
23573      */
23574     updateToolbar: function(){
23575
23576         if(!this.editorcore.activated){
23577             this.editor.onFirstFocus(); // is this neeed?
23578             return;
23579         }
23580
23581         var btns = this.buttons; 
23582         var doc = this.editorcore.doc;
23583         btns.get('bold').setActive(doc.queryCommandState('bold'));
23584         btns.get('italic').setActive(doc.queryCommandState('italic'));
23585         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23586         
23587         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23588         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23589         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23590         
23591         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23592         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23593          /*
23594         
23595         var ans = this.editorcore.getAllAncestors();
23596         if (this.formatCombo) {
23597             
23598             
23599             var store = this.formatCombo.store;
23600             this.formatCombo.setValue("");
23601             for (var i =0; i < ans.length;i++) {
23602                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23603                     // select it..
23604                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23605                     break;
23606                 }
23607             }
23608         }
23609         
23610         
23611         
23612         // hides menus... - so this cant be on a menu...
23613         Roo.bootstrap.MenuMgr.hideAll();
23614         */
23615         Roo.bootstrap.MenuMgr.hideAll();
23616         //this.editorsyncValue();
23617     },
23618     onFirstFocus: function() {
23619         this.buttons.each(function(item){
23620            item.enable();
23621         });
23622     },
23623     toggleSourceEdit : function(sourceEditMode){
23624         
23625           
23626         if(sourceEditMode){
23627             Roo.log("disabling buttons");
23628            this.buttons.each( function(item){
23629                 if(item.cmd != 'pencil'){
23630                     item.disable();
23631                 }
23632             });
23633           
23634         }else{
23635             Roo.log("enabling buttons");
23636             if(this.editorcore.initialized){
23637                 this.buttons.each( function(item){
23638                     item.enable();
23639                 });
23640             }
23641             
23642         }
23643         Roo.log("calling toggole on editor");
23644         // tell the editor that it's been pressed..
23645         this.editor.toggleSourceEdit(sourceEditMode);
23646        
23647     }
23648 });
23649
23650
23651
23652
23653
23654 /**
23655  * @class Roo.bootstrap.Table.AbstractSelectionModel
23656  * @extends Roo.util.Observable
23657  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23658  * implemented by descendant classes.  This class should not be directly instantiated.
23659  * @constructor
23660  */
23661 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23662     this.locked = false;
23663     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23664 };
23665
23666
23667 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23668     /** @ignore Called by the grid automatically. Do not call directly. */
23669     init : function(grid){
23670         this.grid = grid;
23671         this.initEvents();
23672     },
23673
23674     /**
23675      * Locks the selections.
23676      */
23677     lock : function(){
23678         this.locked = true;
23679     },
23680
23681     /**
23682      * Unlocks the selections.
23683      */
23684     unlock : function(){
23685         this.locked = false;
23686     },
23687
23688     /**
23689      * Returns true if the selections are locked.
23690      * @return {Boolean}
23691      */
23692     isLocked : function(){
23693         return this.locked;
23694     }
23695 });
23696 /**
23697  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23698  * @class Roo.bootstrap.Table.RowSelectionModel
23699  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23700  * It supports multiple selections and keyboard selection/navigation. 
23701  * @constructor
23702  * @param {Object} config
23703  */
23704
23705 Roo.bootstrap.Table.RowSelectionModel = function(config){
23706     Roo.apply(this, config);
23707     this.selections = new Roo.util.MixedCollection(false, function(o){
23708         return o.id;
23709     });
23710
23711     this.last = false;
23712     this.lastActive = false;
23713
23714     this.addEvents({
23715         /**
23716              * @event selectionchange
23717              * Fires when the selection changes
23718              * @param {SelectionModel} this
23719              */
23720             "selectionchange" : true,
23721         /**
23722              * @event afterselectionchange
23723              * Fires after the selection changes (eg. by key press or clicking)
23724              * @param {SelectionModel} this
23725              */
23726             "afterselectionchange" : true,
23727         /**
23728              * @event beforerowselect
23729              * Fires when a row is selected being selected, return false to cancel.
23730              * @param {SelectionModel} this
23731              * @param {Number} rowIndex The selected index
23732              * @param {Boolean} keepExisting False if other selections will be cleared
23733              */
23734             "beforerowselect" : true,
23735         /**
23736              * @event rowselect
23737              * Fires when a row is selected.
23738              * @param {SelectionModel} this
23739              * @param {Number} rowIndex The selected index
23740              * @param {Roo.data.Record} r The record
23741              */
23742             "rowselect" : true,
23743         /**
23744              * @event rowdeselect
23745              * Fires when a row is deselected.
23746              * @param {SelectionModel} this
23747              * @param {Number} rowIndex The selected index
23748              */
23749         "rowdeselect" : true
23750     });
23751     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23752     this.locked = false;
23753  };
23754
23755 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23756     /**
23757      * @cfg {Boolean} singleSelect
23758      * True to allow selection of only one row at a time (defaults to false)
23759      */
23760     singleSelect : false,
23761
23762     // private
23763     initEvents : function()
23764     {
23765
23766         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23767         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23768         //}else{ // allow click to work like normal
23769          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23770         //}
23771         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23772         this.grid.on("rowclick", this.handleMouseDown, this);
23773         
23774         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23775             "up" : function(e){
23776                 if(!e.shiftKey){
23777                     this.selectPrevious(e.shiftKey);
23778                 }else if(this.last !== false && this.lastActive !== false){
23779                     var last = this.last;
23780                     this.selectRange(this.last,  this.lastActive-1);
23781                     this.grid.getView().focusRow(this.lastActive);
23782                     if(last !== false){
23783                         this.last = last;
23784                     }
23785                 }else{
23786                     this.selectFirstRow();
23787                 }
23788                 this.fireEvent("afterselectionchange", this);
23789             },
23790             "down" : function(e){
23791                 if(!e.shiftKey){
23792                     this.selectNext(e.shiftKey);
23793                 }else if(this.last !== false && this.lastActive !== false){
23794                     var last = this.last;
23795                     this.selectRange(this.last,  this.lastActive+1);
23796                     this.grid.getView().focusRow(this.lastActive);
23797                     if(last !== false){
23798                         this.last = last;
23799                     }
23800                 }else{
23801                     this.selectFirstRow();
23802                 }
23803                 this.fireEvent("afterselectionchange", this);
23804             },
23805             scope: this
23806         });
23807         this.grid.store.on('load', function(){
23808             this.selections.clear();
23809         },this);
23810         /*
23811         var view = this.grid.view;
23812         view.on("refresh", this.onRefresh, this);
23813         view.on("rowupdated", this.onRowUpdated, this);
23814         view.on("rowremoved", this.onRemove, this);
23815         */
23816     },
23817
23818     // private
23819     onRefresh : function()
23820     {
23821         var ds = this.grid.store, i, v = this.grid.view;
23822         var s = this.selections;
23823         s.each(function(r){
23824             if((i = ds.indexOfId(r.id)) != -1){
23825                 v.onRowSelect(i);
23826             }else{
23827                 s.remove(r);
23828             }
23829         });
23830     },
23831
23832     // private
23833     onRemove : function(v, index, r){
23834         this.selections.remove(r);
23835     },
23836
23837     // private
23838     onRowUpdated : function(v, index, r){
23839         if(this.isSelected(r)){
23840             v.onRowSelect(index);
23841         }
23842     },
23843
23844     /**
23845      * Select records.
23846      * @param {Array} records The records to select
23847      * @param {Boolean} keepExisting (optional) True to keep existing selections
23848      */
23849     selectRecords : function(records, keepExisting)
23850     {
23851         if(!keepExisting){
23852             this.clearSelections();
23853         }
23854             var ds = this.grid.store;
23855         for(var i = 0, len = records.length; i < len; i++){
23856             this.selectRow(ds.indexOf(records[i]), true);
23857         }
23858     },
23859
23860     /**
23861      * Gets the number of selected rows.
23862      * @return {Number}
23863      */
23864     getCount : function(){
23865         return this.selections.length;
23866     },
23867
23868     /**
23869      * Selects the first row in the grid.
23870      */
23871     selectFirstRow : function(){
23872         this.selectRow(0);
23873     },
23874
23875     /**
23876      * Select the last row.
23877      * @param {Boolean} keepExisting (optional) True to keep existing selections
23878      */
23879     selectLastRow : function(keepExisting){
23880         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23881         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23882     },
23883
23884     /**
23885      * Selects the row immediately following the last selected row.
23886      * @param {Boolean} keepExisting (optional) True to keep existing selections
23887      */
23888     selectNext : function(keepExisting)
23889     {
23890             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23891             this.selectRow(this.last+1, keepExisting);
23892             this.grid.getView().focusRow(this.last);
23893         }
23894     },
23895
23896     /**
23897      * Selects the row that precedes the last selected row.
23898      * @param {Boolean} keepExisting (optional) True to keep existing selections
23899      */
23900     selectPrevious : function(keepExisting){
23901         if(this.last){
23902             this.selectRow(this.last-1, keepExisting);
23903             this.grid.getView().focusRow(this.last);
23904         }
23905     },
23906
23907     /**
23908      * Returns the selected records
23909      * @return {Array} Array of selected records
23910      */
23911     getSelections : function(){
23912         return [].concat(this.selections.items);
23913     },
23914
23915     /**
23916      * Returns the first selected record.
23917      * @return {Record}
23918      */
23919     getSelected : function(){
23920         return this.selections.itemAt(0);
23921     },
23922
23923
23924     /**
23925      * Clears all selections.
23926      */
23927     clearSelections : function(fast)
23928     {
23929         if(this.locked) {
23930             return;
23931         }
23932         if(fast !== true){
23933                 var ds = this.grid.store;
23934             var s = this.selections;
23935             s.each(function(r){
23936                 this.deselectRow(ds.indexOfId(r.id));
23937             }, this);
23938             s.clear();
23939         }else{
23940             this.selections.clear();
23941         }
23942         this.last = false;
23943     },
23944
23945
23946     /**
23947      * Selects all rows.
23948      */
23949     selectAll : function(){
23950         if(this.locked) {
23951             return;
23952         }
23953         this.selections.clear();
23954         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23955             this.selectRow(i, true);
23956         }
23957     },
23958
23959     /**
23960      * Returns True if there is a selection.
23961      * @return {Boolean}
23962      */
23963     hasSelection : function(){
23964         return this.selections.length > 0;
23965     },
23966
23967     /**
23968      * Returns True if the specified row is selected.
23969      * @param {Number/Record} record The record or index of the record to check
23970      * @return {Boolean}
23971      */
23972     isSelected : function(index){
23973             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23974         return (r && this.selections.key(r.id) ? true : false);
23975     },
23976
23977     /**
23978      * Returns True if the specified record id is selected.
23979      * @param {String} id The id of record to check
23980      * @return {Boolean}
23981      */
23982     isIdSelected : function(id){
23983         return (this.selections.key(id) ? true : false);
23984     },
23985
23986
23987     // private
23988     handleMouseDBClick : function(e, t){
23989         
23990     },
23991     // private
23992     handleMouseDown : function(e, t)
23993     {
23994             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23995         if(this.isLocked() || rowIndex < 0 ){
23996             return;
23997         };
23998         if(e.shiftKey && this.last !== false){
23999             var last = this.last;
24000             this.selectRange(last, rowIndex, e.ctrlKey);
24001             this.last = last; // reset the last
24002             t.focus();
24003     
24004         }else{
24005             var isSelected = this.isSelected(rowIndex);
24006             //Roo.log("select row:" + rowIndex);
24007             if(isSelected){
24008                 this.deselectRow(rowIndex);
24009             } else {
24010                         this.selectRow(rowIndex, true);
24011             }
24012     
24013             /*
24014                 if(e.button !== 0 && isSelected){
24015                 alert('rowIndex 2: ' + rowIndex);
24016                     view.focusRow(rowIndex);
24017                 }else if(e.ctrlKey && isSelected){
24018                     this.deselectRow(rowIndex);
24019                 }else if(!isSelected){
24020                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24021                     view.focusRow(rowIndex);
24022                 }
24023             */
24024         }
24025         this.fireEvent("afterselectionchange", this);
24026     },
24027     // private
24028     handleDragableRowClick :  function(grid, rowIndex, e) 
24029     {
24030         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24031             this.selectRow(rowIndex, false);
24032             grid.view.focusRow(rowIndex);
24033              this.fireEvent("afterselectionchange", this);
24034         }
24035     },
24036     
24037     /**
24038      * Selects multiple rows.
24039      * @param {Array} rows Array of the indexes of the row to select
24040      * @param {Boolean} keepExisting (optional) True to keep existing selections
24041      */
24042     selectRows : function(rows, keepExisting){
24043         if(!keepExisting){
24044             this.clearSelections();
24045         }
24046         for(var i = 0, len = rows.length; i < len; i++){
24047             this.selectRow(rows[i], true);
24048         }
24049     },
24050
24051     /**
24052      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24053      * @param {Number} startRow The index of the first row in the range
24054      * @param {Number} endRow The index of the last row in the range
24055      * @param {Boolean} keepExisting (optional) True to retain existing selections
24056      */
24057     selectRange : function(startRow, endRow, keepExisting){
24058         if(this.locked) {
24059             return;
24060         }
24061         if(!keepExisting){
24062             this.clearSelections();
24063         }
24064         if(startRow <= endRow){
24065             for(var i = startRow; i <= endRow; i++){
24066                 this.selectRow(i, true);
24067             }
24068         }else{
24069             for(var i = startRow; i >= endRow; i--){
24070                 this.selectRow(i, true);
24071             }
24072         }
24073     },
24074
24075     /**
24076      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24077      * @param {Number} startRow The index of the first row in the range
24078      * @param {Number} endRow The index of the last row in the range
24079      */
24080     deselectRange : function(startRow, endRow, preventViewNotify){
24081         if(this.locked) {
24082             return;
24083         }
24084         for(var i = startRow; i <= endRow; i++){
24085             this.deselectRow(i, preventViewNotify);
24086         }
24087     },
24088
24089     /**
24090      * Selects a row.
24091      * @param {Number} row The index of the row to select
24092      * @param {Boolean} keepExisting (optional) True to keep existing selections
24093      */
24094     selectRow : function(index, keepExisting, preventViewNotify)
24095     {
24096             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24097             return;
24098         }
24099         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24100             if(!keepExisting || this.singleSelect){
24101                 this.clearSelections();
24102             }
24103             
24104             var r = this.grid.store.getAt(index);
24105             //console.log('selectRow - record id :' + r.id);
24106             
24107             this.selections.add(r);
24108             this.last = this.lastActive = index;
24109             if(!preventViewNotify){
24110                 var proxy = new Roo.Element(
24111                                 this.grid.getRowDom(index)
24112                 );
24113                 proxy.addClass('bg-info info');
24114             }
24115             this.fireEvent("rowselect", this, index, r);
24116             this.fireEvent("selectionchange", this);
24117         }
24118     },
24119
24120     /**
24121      * Deselects a row.
24122      * @param {Number} row The index of the row to deselect
24123      */
24124     deselectRow : function(index, preventViewNotify)
24125     {
24126         if(this.locked) {
24127             return;
24128         }
24129         if(this.last == index){
24130             this.last = false;
24131         }
24132         if(this.lastActive == index){
24133             this.lastActive = false;
24134         }
24135         
24136         var r = this.grid.store.getAt(index);
24137         if (!r) {
24138             return;
24139         }
24140         
24141         this.selections.remove(r);
24142         //.console.log('deselectRow - record id :' + r.id);
24143         if(!preventViewNotify){
24144         
24145             var proxy = new Roo.Element(
24146                 this.grid.getRowDom(index)
24147             );
24148             proxy.removeClass('bg-info info');
24149         }
24150         this.fireEvent("rowdeselect", this, index);
24151         this.fireEvent("selectionchange", this);
24152     },
24153
24154     // private
24155     restoreLast : function(){
24156         if(this._last){
24157             this.last = this._last;
24158         }
24159     },
24160
24161     // private
24162     acceptsNav : function(row, col, cm){
24163         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24164     },
24165
24166     // private
24167     onEditorKey : function(field, e){
24168         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24169         if(k == e.TAB){
24170             e.stopEvent();
24171             ed.completeEdit();
24172             if(e.shiftKey){
24173                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24174             }else{
24175                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24176             }
24177         }else if(k == e.ENTER && !e.ctrlKey){
24178             e.stopEvent();
24179             ed.completeEdit();
24180             if(e.shiftKey){
24181                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24182             }else{
24183                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24184             }
24185         }else if(k == e.ESC){
24186             ed.cancelEdit();
24187         }
24188         if(newCell){
24189             g.startEditing(newCell[0], newCell[1]);
24190         }
24191     }
24192 });
24193 /*
24194  * Based on:
24195  * Ext JS Library 1.1.1
24196  * Copyright(c) 2006-2007, Ext JS, LLC.
24197  *
24198  * Originally Released Under LGPL - original licence link has changed is not relivant.
24199  *
24200  * Fork - LGPL
24201  * <script type="text/javascript">
24202  */
24203  
24204 /**
24205  * @class Roo.bootstrap.PagingToolbar
24206  * @extends Roo.bootstrap.NavSimplebar
24207  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24208  * @constructor
24209  * Create a new PagingToolbar
24210  * @param {Object} config The config object
24211  * @param {Roo.data.Store} store
24212  */
24213 Roo.bootstrap.PagingToolbar = function(config)
24214 {
24215     // old args format still supported... - xtype is prefered..
24216         // created from xtype...
24217     
24218     this.ds = config.dataSource;
24219     
24220     if (config.store && !this.ds) {
24221         this.store= Roo.factory(config.store, Roo.data);
24222         this.ds = this.store;
24223         this.ds.xmodule = this.xmodule || false;
24224     }
24225     
24226     this.toolbarItems = [];
24227     if (config.items) {
24228         this.toolbarItems = config.items;
24229     }
24230     
24231     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24232     
24233     this.cursor = 0;
24234     
24235     if (this.ds) { 
24236         this.bind(this.ds);
24237     }
24238     
24239     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24240     
24241 };
24242
24243 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24244     /**
24245      * @cfg {Roo.data.Store} dataSource
24246      * The underlying data store providing the paged data
24247      */
24248     /**
24249      * @cfg {String/HTMLElement/Element} container
24250      * container The id or element that will contain the toolbar
24251      */
24252     /**
24253      * @cfg {Boolean} displayInfo
24254      * True to display the displayMsg (defaults to false)
24255      */
24256     /**
24257      * @cfg {Number} pageSize
24258      * The number of records to display per page (defaults to 20)
24259      */
24260     pageSize: 20,
24261     /**
24262      * @cfg {String} displayMsg
24263      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24264      */
24265     displayMsg : 'Displaying {0} - {1} of {2}',
24266     /**
24267      * @cfg {String} emptyMsg
24268      * The message to display when no records are found (defaults to "No data to display")
24269      */
24270     emptyMsg : 'No data to display',
24271     /**
24272      * Customizable piece of the default paging text (defaults to "Page")
24273      * @type String
24274      */
24275     beforePageText : "Page",
24276     /**
24277      * Customizable piece of the default paging text (defaults to "of %0")
24278      * @type String
24279      */
24280     afterPageText : "of {0}",
24281     /**
24282      * Customizable piece of the default paging text (defaults to "First Page")
24283      * @type String
24284      */
24285     firstText : "First Page",
24286     /**
24287      * Customizable piece of the default paging text (defaults to "Previous Page")
24288      * @type String
24289      */
24290     prevText : "Previous Page",
24291     /**
24292      * Customizable piece of the default paging text (defaults to "Next Page")
24293      * @type String
24294      */
24295     nextText : "Next Page",
24296     /**
24297      * Customizable piece of the default paging text (defaults to "Last Page")
24298      * @type String
24299      */
24300     lastText : "Last Page",
24301     /**
24302      * Customizable piece of the default paging text (defaults to "Refresh")
24303      * @type String
24304      */
24305     refreshText : "Refresh",
24306
24307     buttons : false,
24308     // private
24309     onRender : function(ct, position) 
24310     {
24311         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24312         this.navgroup.parentId = this.id;
24313         this.navgroup.onRender(this.el, null);
24314         // add the buttons to the navgroup
24315         
24316         if(this.displayInfo){
24317             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24318             this.displayEl = this.el.select('.x-paging-info', true).first();
24319 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24320 //            this.displayEl = navel.el.select('span',true).first();
24321         }
24322         
24323         var _this = this;
24324         
24325         if(this.buttons){
24326             Roo.each(_this.buttons, function(e){ // this might need to use render????
24327                Roo.factory(e).onRender(_this.el, null);
24328             });
24329         }
24330             
24331         Roo.each(_this.toolbarItems, function(e) {
24332             _this.navgroup.addItem(e);
24333         });
24334         
24335         
24336         this.first = this.navgroup.addItem({
24337             tooltip: this.firstText,
24338             cls: "prev",
24339             icon : 'fa fa-backward',
24340             disabled: true,
24341             preventDefault: true,
24342             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24343         });
24344         
24345         this.prev =  this.navgroup.addItem({
24346             tooltip: this.prevText,
24347             cls: "prev",
24348             icon : 'fa fa-step-backward',
24349             disabled: true,
24350             preventDefault: true,
24351             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24352         });
24353     //this.addSeparator();
24354         
24355         
24356         var field = this.navgroup.addItem( {
24357             tagtype : 'span',
24358             cls : 'x-paging-position',
24359             
24360             html : this.beforePageText  +
24361                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24362                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24363          } ); //?? escaped?
24364         
24365         this.field = field.el.select('input', true).first();
24366         this.field.on("keydown", this.onPagingKeydown, this);
24367         this.field.on("focus", function(){this.dom.select();});
24368     
24369     
24370         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24371         //this.field.setHeight(18);
24372         //this.addSeparator();
24373         this.next = this.navgroup.addItem({
24374             tooltip: this.nextText,
24375             cls: "next",
24376             html : ' <i class="fa fa-step-forward">',
24377             disabled: true,
24378             preventDefault: true,
24379             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24380         });
24381         this.last = this.navgroup.addItem({
24382             tooltip: this.lastText,
24383             icon : 'fa fa-forward',
24384             cls: "next",
24385             disabled: true,
24386             preventDefault: true,
24387             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24388         });
24389     //this.addSeparator();
24390         this.loading = this.navgroup.addItem({
24391             tooltip: this.refreshText,
24392             icon: 'fa fa-refresh',
24393             preventDefault: true,
24394             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24395         });
24396         
24397     },
24398
24399     // private
24400     updateInfo : function(){
24401         if(this.displayEl){
24402             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24403             var msg = count == 0 ?
24404                 this.emptyMsg :
24405                 String.format(
24406                     this.displayMsg,
24407                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24408                 );
24409             this.displayEl.update(msg);
24410         }
24411     },
24412
24413     // private
24414     onLoad : function(ds, r, o)
24415     {
24416         this.cursor = o.params ? o.params.start : 0;
24417         var d = this.getPageData(),
24418             ap = d.activePage,
24419             ps = d.pages;
24420         
24421         
24422         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24423         this.field.dom.value = ap;
24424         this.first.setDisabled(ap == 1);
24425         this.prev.setDisabled(ap == 1);
24426         this.next.setDisabled(ap == ps);
24427         this.last.setDisabled(ap == ps);
24428         this.loading.enable();
24429         this.updateInfo();
24430     },
24431
24432     // private
24433     getPageData : function(){
24434         var total = this.ds.getTotalCount();
24435         return {
24436             total : total,
24437             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24438             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24439         };
24440     },
24441
24442     // private
24443     onLoadError : function(){
24444         this.loading.enable();
24445     },
24446
24447     // private
24448     onPagingKeydown : function(e){
24449         var k = e.getKey();
24450         var d = this.getPageData();
24451         if(k == e.RETURN){
24452             var v = this.field.dom.value, pageNum;
24453             if(!v || isNaN(pageNum = parseInt(v, 10))){
24454                 this.field.dom.value = d.activePage;
24455                 return;
24456             }
24457             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24458             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24459             e.stopEvent();
24460         }
24461         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))
24462         {
24463           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24464           this.field.dom.value = pageNum;
24465           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24466           e.stopEvent();
24467         }
24468         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24469         {
24470           var v = this.field.dom.value, pageNum; 
24471           var increment = (e.shiftKey) ? 10 : 1;
24472           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24473                 increment *= -1;
24474           }
24475           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24476             this.field.dom.value = d.activePage;
24477             return;
24478           }
24479           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24480           {
24481             this.field.dom.value = parseInt(v, 10) + increment;
24482             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24483             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24484           }
24485           e.stopEvent();
24486         }
24487     },
24488
24489     // private
24490     beforeLoad : function(){
24491         if(this.loading){
24492             this.loading.disable();
24493         }
24494     },
24495
24496     // private
24497     onClick : function(which){
24498         
24499         var ds = this.ds;
24500         if (!ds) {
24501             return;
24502         }
24503         
24504         switch(which){
24505             case "first":
24506                 ds.load({params:{start: 0, limit: this.pageSize}});
24507             break;
24508             case "prev":
24509                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24510             break;
24511             case "next":
24512                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24513             break;
24514             case "last":
24515                 var total = ds.getTotalCount();
24516                 var extra = total % this.pageSize;
24517                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24518                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24519             break;
24520             case "refresh":
24521                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24522             break;
24523         }
24524     },
24525
24526     /**
24527      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24528      * @param {Roo.data.Store} store The data store to unbind
24529      */
24530     unbind : function(ds){
24531         ds.un("beforeload", this.beforeLoad, this);
24532         ds.un("load", this.onLoad, this);
24533         ds.un("loadexception", this.onLoadError, this);
24534         ds.un("remove", this.updateInfo, this);
24535         ds.un("add", this.updateInfo, this);
24536         this.ds = undefined;
24537     },
24538
24539     /**
24540      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24541      * @param {Roo.data.Store} store The data store to bind
24542      */
24543     bind : function(ds){
24544         ds.on("beforeload", this.beforeLoad, this);
24545         ds.on("load", this.onLoad, this);
24546         ds.on("loadexception", this.onLoadError, this);
24547         ds.on("remove", this.updateInfo, this);
24548         ds.on("add", this.updateInfo, this);
24549         this.ds = ds;
24550     }
24551 });/*
24552  * - LGPL
24553  *
24554  * element
24555  * 
24556  */
24557
24558 /**
24559  * @class Roo.bootstrap.MessageBar
24560  * @extends Roo.bootstrap.Component
24561  * Bootstrap MessageBar class
24562  * @cfg {String} html contents of the MessageBar
24563  * @cfg {String} weight (info | success | warning | danger) default info
24564  * @cfg {String} beforeClass insert the bar before the given class
24565  * @cfg {Boolean} closable (true | false) default false
24566  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24567  * 
24568  * @constructor
24569  * Create a new Element
24570  * @param {Object} config The config object
24571  */
24572
24573 Roo.bootstrap.MessageBar = function(config){
24574     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24575 };
24576
24577 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24578     
24579     html: '',
24580     weight: 'info',
24581     closable: false,
24582     fixed: false,
24583     beforeClass: 'bootstrap-sticky-wrap',
24584     
24585     getAutoCreate : function(){
24586         
24587         var cfg = {
24588             tag: 'div',
24589             cls: 'alert alert-dismissable alert-' + this.weight,
24590             cn: [
24591                 {
24592                     tag: 'span',
24593                     cls: 'message',
24594                     html: this.html || ''
24595                 }
24596             ]
24597         };
24598         
24599         if(this.fixed){
24600             cfg.cls += ' alert-messages-fixed';
24601         }
24602         
24603         if(this.closable){
24604             cfg.cn.push({
24605                 tag: 'button',
24606                 cls: 'close',
24607                 html: 'x'
24608             });
24609         }
24610         
24611         return cfg;
24612     },
24613     
24614     onRender : function(ct, position)
24615     {
24616         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24617         
24618         if(!this.el){
24619             var cfg = Roo.apply({},  this.getAutoCreate());
24620             cfg.id = Roo.id();
24621             
24622             if (this.cls) {
24623                 cfg.cls += ' ' + this.cls;
24624             }
24625             if (this.style) {
24626                 cfg.style = this.style;
24627             }
24628             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24629             
24630             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24631         }
24632         
24633         this.el.select('>button.close').on('click', this.hide, this);
24634         
24635     },
24636     
24637     show : function()
24638     {
24639         if (!this.rendered) {
24640             this.render();
24641         }
24642         
24643         this.el.show();
24644         
24645         this.fireEvent('show', this);
24646         
24647     },
24648     
24649     hide : function()
24650     {
24651         if (!this.rendered) {
24652             this.render();
24653         }
24654         
24655         this.el.hide();
24656         
24657         this.fireEvent('hide', this);
24658     },
24659     
24660     update : function()
24661     {
24662 //        var e = this.el.dom.firstChild;
24663 //        
24664 //        if(this.closable){
24665 //            e = e.nextSibling;
24666 //        }
24667 //        
24668 //        e.data = this.html || '';
24669
24670         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24671     }
24672    
24673 });
24674
24675  
24676
24677      /*
24678  * - LGPL
24679  *
24680  * Graph
24681  * 
24682  */
24683
24684
24685 /**
24686  * @class Roo.bootstrap.Graph
24687  * @extends Roo.bootstrap.Component
24688  * Bootstrap Graph class
24689 > Prameters
24690  -sm {number} sm 4
24691  -md {number} md 5
24692  @cfg {String} graphtype  bar | vbar | pie
24693  @cfg {number} g_x coodinator | centre x (pie)
24694  @cfg {number} g_y coodinator | centre y (pie)
24695  @cfg {number} g_r radius (pie)
24696  @cfg {number} g_height height of the chart (respected by all elements in the set)
24697  @cfg {number} g_width width of the chart (respected by all elements in the set)
24698  @cfg {Object} title The title of the chart
24699     
24700  -{Array}  values
24701  -opts (object) options for the chart 
24702      o {
24703      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24704      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24705      o vgutter (number)
24706      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.
24707      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24708      o to
24709      o stretch (boolean)
24710      o }
24711  -opts (object) options for the pie
24712      o{
24713      o cut
24714      o startAngle (number)
24715      o endAngle (number)
24716      } 
24717  *
24718  * @constructor
24719  * Create a new Input
24720  * @param {Object} config The config object
24721  */
24722
24723 Roo.bootstrap.Graph = function(config){
24724     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24725     
24726     this.addEvents({
24727         // img events
24728         /**
24729          * @event click
24730          * The img click event for the img.
24731          * @param {Roo.EventObject} e
24732          */
24733         "click" : true
24734     });
24735 };
24736
24737 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24738     
24739     sm: 4,
24740     md: 5,
24741     graphtype: 'bar',
24742     g_height: 250,
24743     g_width: 400,
24744     g_x: 50,
24745     g_y: 50,
24746     g_r: 30,
24747     opts:{
24748         //g_colors: this.colors,
24749         g_type: 'soft',
24750         g_gutter: '20%'
24751
24752     },
24753     title : false,
24754
24755     getAutoCreate : function(){
24756         
24757         var cfg = {
24758             tag: 'div',
24759             html : null
24760         };
24761         
24762         
24763         return  cfg;
24764     },
24765
24766     onRender : function(ct,position){
24767         
24768         
24769         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24770         
24771         if (typeof(Raphael) == 'undefined') {
24772             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24773             return;
24774         }
24775         
24776         this.raphael = Raphael(this.el.dom);
24777         
24778                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24779                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24780                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24781                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24782                 /*
24783                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24784                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24785                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24786                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24787                 
24788                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24789                 r.barchart(330, 10, 300, 220, data1);
24790                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24791                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24792                 */
24793                 
24794                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24795                 // r.barchart(30, 30, 560, 250,  xdata, {
24796                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24797                 //     axis : "0 0 1 1",
24798                 //     axisxlabels :  xdata
24799                 //     //yvalues : cols,
24800                    
24801                 // });
24802 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24803 //        
24804 //        this.load(null,xdata,{
24805 //                axis : "0 0 1 1",
24806 //                axisxlabels :  xdata
24807 //                });
24808
24809     },
24810
24811     load : function(graphtype,xdata,opts)
24812     {
24813         this.raphael.clear();
24814         if(!graphtype) {
24815             graphtype = this.graphtype;
24816         }
24817         if(!opts){
24818             opts = this.opts;
24819         }
24820         var r = this.raphael,
24821             fin = function () {
24822                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24823             },
24824             fout = function () {
24825                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24826             },
24827             pfin = function() {
24828                 this.sector.stop();
24829                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24830
24831                 if (this.label) {
24832                     this.label[0].stop();
24833                     this.label[0].attr({ r: 7.5 });
24834                     this.label[1].attr({ "font-weight": 800 });
24835                 }
24836             },
24837             pfout = function() {
24838                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24839
24840                 if (this.label) {
24841                     this.label[0].animate({ r: 5 }, 500, "bounce");
24842                     this.label[1].attr({ "font-weight": 400 });
24843                 }
24844             };
24845
24846         switch(graphtype){
24847             case 'bar':
24848                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24849                 break;
24850             case 'hbar':
24851                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24852                 break;
24853             case 'pie':
24854 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24855 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24856 //            
24857                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24858                 
24859                 break;
24860
24861         }
24862         
24863         if(this.title){
24864             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24865         }
24866         
24867     },
24868     
24869     setTitle: function(o)
24870     {
24871         this.title = o;
24872     },
24873     
24874     initEvents: function() {
24875         
24876         if(!this.href){
24877             this.el.on('click', this.onClick, this);
24878         }
24879     },
24880     
24881     onClick : function(e)
24882     {
24883         Roo.log('img onclick');
24884         this.fireEvent('click', this, e);
24885     }
24886    
24887 });
24888
24889  
24890 /*
24891  * - LGPL
24892  *
24893  * numberBox
24894  * 
24895  */
24896 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24897
24898 /**
24899  * @class Roo.bootstrap.dash.NumberBox
24900  * @extends Roo.bootstrap.Component
24901  * Bootstrap NumberBox class
24902  * @cfg {String} headline Box headline
24903  * @cfg {String} content Box content
24904  * @cfg {String} icon Box icon
24905  * @cfg {String} footer Footer text
24906  * @cfg {String} fhref Footer href
24907  * 
24908  * @constructor
24909  * Create a new NumberBox
24910  * @param {Object} config The config object
24911  */
24912
24913
24914 Roo.bootstrap.dash.NumberBox = function(config){
24915     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24916     
24917 };
24918
24919 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24920     
24921     headline : '',
24922     content : '',
24923     icon : '',
24924     footer : '',
24925     fhref : '',
24926     ficon : '',
24927     
24928     getAutoCreate : function(){
24929         
24930         var cfg = {
24931             tag : 'div',
24932             cls : 'small-box ',
24933             cn : [
24934                 {
24935                     tag : 'div',
24936                     cls : 'inner',
24937                     cn :[
24938                         {
24939                             tag : 'h3',
24940                             cls : 'roo-headline',
24941                             html : this.headline
24942                         },
24943                         {
24944                             tag : 'p',
24945                             cls : 'roo-content',
24946                             html : this.content
24947                         }
24948                     ]
24949                 }
24950             ]
24951         };
24952         
24953         if(this.icon){
24954             cfg.cn.push({
24955                 tag : 'div',
24956                 cls : 'icon',
24957                 cn :[
24958                     {
24959                         tag : 'i',
24960                         cls : 'ion ' + this.icon
24961                     }
24962                 ]
24963             });
24964         }
24965         
24966         if(this.footer){
24967             var footer = {
24968                 tag : 'a',
24969                 cls : 'small-box-footer',
24970                 href : this.fhref || '#',
24971                 html : this.footer
24972             };
24973             
24974             cfg.cn.push(footer);
24975             
24976         }
24977         
24978         return  cfg;
24979     },
24980
24981     onRender : function(ct,position){
24982         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24983
24984
24985        
24986                 
24987     },
24988
24989     setHeadline: function (value)
24990     {
24991         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24992     },
24993     
24994     setFooter: function (value, href)
24995     {
24996         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24997         
24998         if(href){
24999             this.el.select('a.small-box-footer',true).first().attr('href', href);
25000         }
25001         
25002     },
25003
25004     setContent: function (value)
25005     {
25006         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25007     },
25008
25009     initEvents: function() 
25010     {   
25011         
25012     }
25013     
25014 });
25015
25016  
25017 /*
25018  * - LGPL
25019  *
25020  * TabBox
25021  * 
25022  */
25023 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25024
25025 /**
25026  * @class Roo.bootstrap.dash.TabBox
25027  * @extends Roo.bootstrap.Component
25028  * Bootstrap TabBox class
25029  * @cfg {String} title Title of the TabBox
25030  * @cfg {String} icon Icon of the TabBox
25031  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25032  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25033  * 
25034  * @constructor
25035  * Create a new TabBox
25036  * @param {Object} config The config object
25037  */
25038
25039
25040 Roo.bootstrap.dash.TabBox = function(config){
25041     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25042     this.addEvents({
25043         // raw events
25044         /**
25045          * @event addpane
25046          * When a pane is added
25047          * @param {Roo.bootstrap.dash.TabPane} pane
25048          */
25049         "addpane" : true,
25050         /**
25051          * @event activatepane
25052          * When a pane is activated
25053          * @param {Roo.bootstrap.dash.TabPane} pane
25054          */
25055         "activatepane" : true
25056         
25057          
25058     });
25059     
25060     this.panes = [];
25061 };
25062
25063 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25064
25065     title : '',
25066     icon : false,
25067     showtabs : true,
25068     tabScrollable : false,
25069     
25070     getChildContainer : function()
25071     {
25072         return this.el.select('.tab-content', true).first();
25073     },
25074     
25075     getAutoCreate : function(){
25076         
25077         var header = {
25078             tag: 'li',
25079             cls: 'pull-left header',
25080             html: this.title,
25081             cn : []
25082         };
25083         
25084         if(this.icon){
25085             header.cn.push({
25086                 tag: 'i',
25087                 cls: 'fa ' + this.icon
25088             });
25089         }
25090         
25091         var h = {
25092             tag: 'ul',
25093             cls: 'nav nav-tabs pull-right',
25094             cn: [
25095                 header
25096             ]
25097         };
25098         
25099         if(this.tabScrollable){
25100             h = {
25101                 tag: 'div',
25102                 cls: 'tab-header',
25103                 cn: [
25104                     {
25105                         tag: 'ul',
25106                         cls: 'nav nav-tabs pull-right',
25107                         cn: [
25108                             header
25109                         ]
25110                     }
25111                 ]
25112             };
25113         }
25114         
25115         var cfg = {
25116             tag: 'div',
25117             cls: 'nav-tabs-custom',
25118             cn: [
25119                 h,
25120                 {
25121                     tag: 'div',
25122                     cls: 'tab-content no-padding',
25123                     cn: []
25124                 }
25125             ]
25126         };
25127
25128         return  cfg;
25129     },
25130     initEvents : function()
25131     {
25132         //Roo.log('add add pane handler');
25133         this.on('addpane', this.onAddPane, this);
25134     },
25135      /**
25136      * Updates the box title
25137      * @param {String} html to set the title to.
25138      */
25139     setTitle : function(value)
25140     {
25141         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25142     },
25143     onAddPane : function(pane)
25144     {
25145         this.panes.push(pane);
25146         //Roo.log('addpane');
25147         //Roo.log(pane);
25148         // tabs are rendere left to right..
25149         if(!this.showtabs){
25150             return;
25151         }
25152         
25153         var ctr = this.el.select('.nav-tabs', true).first();
25154          
25155          
25156         var existing = ctr.select('.nav-tab',true);
25157         var qty = existing.getCount();;
25158         
25159         
25160         var tab = ctr.createChild({
25161             tag : 'li',
25162             cls : 'nav-tab' + (qty ? '' : ' active'),
25163             cn : [
25164                 {
25165                     tag : 'a',
25166                     href:'#',
25167                     html : pane.title
25168                 }
25169             ]
25170         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25171         pane.tab = tab;
25172         
25173         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25174         if (!qty) {
25175             pane.el.addClass('active');
25176         }
25177         
25178                 
25179     },
25180     onTabClick : function(ev,un,ob,pane)
25181     {
25182         //Roo.log('tab - prev default');
25183         ev.preventDefault();
25184         
25185         
25186         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25187         pane.tab.addClass('active');
25188         //Roo.log(pane.title);
25189         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25190         // technically we should have a deactivate event.. but maybe add later.
25191         // and it should not de-activate the selected tab...
25192         this.fireEvent('activatepane', pane);
25193         pane.el.addClass('active');
25194         pane.fireEvent('activate');
25195         
25196         
25197     },
25198     
25199     getActivePane : function()
25200     {
25201         var r = false;
25202         Roo.each(this.panes, function(p) {
25203             if(p.el.hasClass('active')){
25204                 r = p;
25205                 return false;
25206             }
25207             
25208             return;
25209         });
25210         
25211         return r;
25212     }
25213     
25214     
25215 });
25216
25217  
25218 /*
25219  * - LGPL
25220  *
25221  * Tab pane
25222  * 
25223  */
25224 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25225 /**
25226  * @class Roo.bootstrap.TabPane
25227  * @extends Roo.bootstrap.Component
25228  * Bootstrap TabPane class
25229  * @cfg {Boolean} active (false | true) Default false
25230  * @cfg {String} title title of panel
25231
25232  * 
25233  * @constructor
25234  * Create a new TabPane
25235  * @param {Object} config The config object
25236  */
25237
25238 Roo.bootstrap.dash.TabPane = function(config){
25239     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25240     
25241     this.addEvents({
25242         // raw events
25243         /**
25244          * @event activate
25245          * When a pane is activated
25246          * @param {Roo.bootstrap.dash.TabPane} pane
25247          */
25248         "activate" : true
25249          
25250     });
25251 };
25252
25253 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25254     
25255     active : false,
25256     title : '',
25257     
25258     // the tabBox that this is attached to.
25259     tab : false,
25260      
25261     getAutoCreate : function() 
25262     {
25263         var cfg = {
25264             tag: 'div',
25265             cls: 'tab-pane'
25266         };
25267         
25268         if(this.active){
25269             cfg.cls += ' active';
25270         }
25271         
25272         return cfg;
25273     },
25274     initEvents  : function()
25275     {
25276         //Roo.log('trigger add pane handler');
25277         this.parent().fireEvent('addpane', this)
25278     },
25279     
25280      /**
25281      * Updates the tab title 
25282      * @param {String} html to set the title to.
25283      */
25284     setTitle: function(str)
25285     {
25286         if (!this.tab) {
25287             return;
25288         }
25289         this.title = str;
25290         this.tab.select('a', true).first().dom.innerHTML = str;
25291         
25292     }
25293     
25294     
25295     
25296 });
25297
25298  
25299
25300
25301  /*
25302  * - LGPL
25303  *
25304  * menu
25305  * 
25306  */
25307 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25308
25309 /**
25310  * @class Roo.bootstrap.menu.Menu
25311  * @extends Roo.bootstrap.Component
25312  * Bootstrap Menu class - container for Menu
25313  * @cfg {String} html Text of the menu
25314  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25315  * @cfg {String} icon Font awesome icon
25316  * @cfg {String} pos Menu align to (top | bottom) default bottom
25317  * 
25318  * 
25319  * @constructor
25320  * Create a new Menu
25321  * @param {Object} config The config object
25322  */
25323
25324
25325 Roo.bootstrap.menu.Menu = function(config){
25326     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25327     
25328     this.addEvents({
25329         /**
25330          * @event beforeshow
25331          * Fires before this menu is displayed
25332          * @param {Roo.bootstrap.menu.Menu} this
25333          */
25334         beforeshow : true,
25335         /**
25336          * @event beforehide
25337          * Fires before this menu is hidden
25338          * @param {Roo.bootstrap.menu.Menu} this
25339          */
25340         beforehide : true,
25341         /**
25342          * @event show
25343          * Fires after this menu is displayed
25344          * @param {Roo.bootstrap.menu.Menu} this
25345          */
25346         show : true,
25347         /**
25348          * @event hide
25349          * Fires after this menu is hidden
25350          * @param {Roo.bootstrap.menu.Menu} this
25351          */
25352         hide : true,
25353         /**
25354          * @event click
25355          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25356          * @param {Roo.bootstrap.menu.Menu} this
25357          * @param {Roo.EventObject} e
25358          */
25359         click : true
25360     });
25361     
25362 };
25363
25364 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25365     
25366     submenu : false,
25367     html : '',
25368     weight : 'default',
25369     icon : false,
25370     pos : 'bottom',
25371     
25372     
25373     getChildContainer : function() {
25374         if(this.isSubMenu){
25375             return this.el;
25376         }
25377         
25378         return this.el.select('ul.dropdown-menu', true).first();  
25379     },
25380     
25381     getAutoCreate : function()
25382     {
25383         var text = [
25384             {
25385                 tag : 'span',
25386                 cls : 'roo-menu-text',
25387                 html : this.html
25388             }
25389         ];
25390         
25391         if(this.icon){
25392             text.unshift({
25393                 tag : 'i',
25394                 cls : 'fa ' + this.icon
25395             })
25396         }
25397         
25398         
25399         var cfg = {
25400             tag : 'div',
25401             cls : 'btn-group',
25402             cn : [
25403                 {
25404                     tag : 'button',
25405                     cls : 'dropdown-button btn btn-' + this.weight,
25406                     cn : text
25407                 },
25408                 {
25409                     tag : 'button',
25410                     cls : 'dropdown-toggle btn btn-' + this.weight,
25411                     cn : [
25412                         {
25413                             tag : 'span',
25414                             cls : 'caret'
25415                         }
25416                     ]
25417                 },
25418                 {
25419                     tag : 'ul',
25420                     cls : 'dropdown-menu'
25421                 }
25422             ]
25423             
25424         };
25425         
25426         if(this.pos == 'top'){
25427             cfg.cls += ' dropup';
25428         }
25429         
25430         if(this.isSubMenu){
25431             cfg = {
25432                 tag : 'ul',
25433                 cls : 'dropdown-menu'
25434             }
25435         }
25436         
25437         return cfg;
25438     },
25439     
25440     onRender : function(ct, position)
25441     {
25442         this.isSubMenu = ct.hasClass('dropdown-submenu');
25443         
25444         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25445     },
25446     
25447     initEvents : function() 
25448     {
25449         if(this.isSubMenu){
25450             return;
25451         }
25452         
25453         this.hidden = true;
25454         
25455         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25456         this.triggerEl.on('click', this.onTriggerPress, this);
25457         
25458         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25459         this.buttonEl.on('click', this.onClick, this);
25460         
25461     },
25462     
25463     list : function()
25464     {
25465         if(this.isSubMenu){
25466             return this.el;
25467         }
25468         
25469         return this.el.select('ul.dropdown-menu', true).first();
25470     },
25471     
25472     onClick : function(e)
25473     {
25474         this.fireEvent("click", this, e);
25475     },
25476     
25477     onTriggerPress  : function(e)
25478     {   
25479         if (this.isVisible()) {
25480             this.hide();
25481         } else {
25482             this.show();
25483         }
25484     },
25485     
25486     isVisible : function(){
25487         return !this.hidden;
25488     },
25489     
25490     show : function()
25491     {
25492         this.fireEvent("beforeshow", this);
25493         
25494         this.hidden = false;
25495         this.el.addClass('open');
25496         
25497         Roo.get(document).on("mouseup", this.onMouseUp, this);
25498         
25499         this.fireEvent("show", this);
25500         
25501         
25502     },
25503     
25504     hide : function()
25505     {
25506         this.fireEvent("beforehide", this);
25507         
25508         this.hidden = true;
25509         this.el.removeClass('open');
25510         
25511         Roo.get(document).un("mouseup", this.onMouseUp);
25512         
25513         this.fireEvent("hide", this);
25514     },
25515     
25516     onMouseUp : function()
25517     {
25518         this.hide();
25519     }
25520     
25521 });
25522
25523  
25524  /*
25525  * - LGPL
25526  *
25527  * menu item
25528  * 
25529  */
25530 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25531
25532 /**
25533  * @class Roo.bootstrap.menu.Item
25534  * @extends Roo.bootstrap.Component
25535  * Bootstrap MenuItem class
25536  * @cfg {Boolean} submenu (true | false) default false
25537  * @cfg {String} html text of the item
25538  * @cfg {String} href the link
25539  * @cfg {Boolean} disable (true | false) default false
25540  * @cfg {Boolean} preventDefault (true | false) default true
25541  * @cfg {String} icon Font awesome icon
25542  * @cfg {String} pos Submenu align to (left | right) default right 
25543  * 
25544  * 
25545  * @constructor
25546  * Create a new Item
25547  * @param {Object} config The config object
25548  */
25549
25550
25551 Roo.bootstrap.menu.Item = function(config){
25552     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25553     this.addEvents({
25554         /**
25555          * @event mouseover
25556          * Fires when the mouse is hovering over this menu
25557          * @param {Roo.bootstrap.menu.Item} this
25558          * @param {Roo.EventObject} e
25559          */
25560         mouseover : true,
25561         /**
25562          * @event mouseout
25563          * Fires when the mouse exits this menu
25564          * @param {Roo.bootstrap.menu.Item} this
25565          * @param {Roo.EventObject} e
25566          */
25567         mouseout : true,
25568         // raw events
25569         /**
25570          * @event click
25571          * The raw click event for the entire grid.
25572          * @param {Roo.EventObject} e
25573          */
25574         click : true
25575     });
25576 };
25577
25578 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25579     
25580     submenu : false,
25581     href : '',
25582     html : '',
25583     preventDefault: true,
25584     disable : false,
25585     icon : false,
25586     pos : 'right',
25587     
25588     getAutoCreate : function()
25589     {
25590         var text = [
25591             {
25592                 tag : 'span',
25593                 cls : 'roo-menu-item-text',
25594                 html : this.html
25595             }
25596         ];
25597         
25598         if(this.icon){
25599             text.unshift({
25600                 tag : 'i',
25601                 cls : 'fa ' + this.icon
25602             })
25603         }
25604         
25605         var cfg = {
25606             tag : 'li',
25607             cn : [
25608                 {
25609                     tag : 'a',
25610                     href : this.href || '#',
25611                     cn : text
25612                 }
25613             ]
25614         };
25615         
25616         if(this.disable){
25617             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25618         }
25619         
25620         if(this.submenu){
25621             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25622             
25623             if(this.pos == 'left'){
25624                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25625             }
25626         }
25627         
25628         return cfg;
25629     },
25630     
25631     initEvents : function() 
25632     {
25633         this.el.on('mouseover', this.onMouseOver, this);
25634         this.el.on('mouseout', this.onMouseOut, this);
25635         
25636         this.el.select('a', true).first().on('click', this.onClick, this);
25637         
25638     },
25639     
25640     onClick : function(e)
25641     {
25642         if(this.preventDefault){
25643             e.preventDefault();
25644         }
25645         
25646         this.fireEvent("click", this, e);
25647     },
25648     
25649     onMouseOver : function(e)
25650     {
25651         if(this.submenu && this.pos == 'left'){
25652             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25653         }
25654         
25655         this.fireEvent("mouseover", this, e);
25656     },
25657     
25658     onMouseOut : function(e)
25659     {
25660         this.fireEvent("mouseout", this, e);
25661     }
25662 });
25663
25664  
25665
25666  /*
25667  * - LGPL
25668  *
25669  * menu separator
25670  * 
25671  */
25672 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25673
25674 /**
25675  * @class Roo.bootstrap.menu.Separator
25676  * @extends Roo.bootstrap.Component
25677  * Bootstrap Separator class
25678  * 
25679  * @constructor
25680  * Create a new Separator
25681  * @param {Object} config The config object
25682  */
25683
25684
25685 Roo.bootstrap.menu.Separator = function(config){
25686     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25687 };
25688
25689 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25690     
25691     getAutoCreate : function(){
25692         var cfg = {
25693             tag : 'li',
25694             cls: 'divider'
25695         };
25696         
25697         return cfg;
25698     }
25699    
25700 });
25701
25702  
25703
25704  /*
25705  * - LGPL
25706  *
25707  * Tooltip
25708  * 
25709  */
25710
25711 /**
25712  * @class Roo.bootstrap.Tooltip
25713  * Bootstrap Tooltip class
25714  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25715  * to determine which dom element triggers the tooltip.
25716  * 
25717  * It needs to add support for additional attributes like tooltip-position
25718  * 
25719  * @constructor
25720  * Create a new Toolti
25721  * @param {Object} config The config object
25722  */
25723
25724 Roo.bootstrap.Tooltip = function(config){
25725     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25726     
25727     this.alignment = Roo.bootstrap.Tooltip.alignment;
25728     
25729     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25730         this.alignment = config.alignment;
25731     }
25732     
25733 };
25734
25735 Roo.apply(Roo.bootstrap.Tooltip, {
25736     /**
25737      * @function init initialize tooltip monitoring.
25738      * @static
25739      */
25740     currentEl : false,
25741     currentTip : false,
25742     currentRegion : false,
25743     
25744     //  init : delay?
25745     
25746     init : function()
25747     {
25748         Roo.get(document).on('mouseover', this.enter ,this);
25749         Roo.get(document).on('mouseout', this.leave, this);
25750          
25751         
25752         this.currentTip = new Roo.bootstrap.Tooltip();
25753     },
25754     
25755     enter : function(ev)
25756     {
25757         var dom = ev.getTarget();
25758         
25759         //Roo.log(['enter',dom]);
25760         var el = Roo.fly(dom);
25761         if (this.currentEl) {
25762             //Roo.log(dom);
25763             //Roo.log(this.currentEl);
25764             //Roo.log(this.currentEl.contains(dom));
25765             if (this.currentEl == el) {
25766                 return;
25767             }
25768             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25769                 return;
25770             }
25771
25772         }
25773         
25774         if (this.currentTip.el) {
25775             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25776         }    
25777         //Roo.log(ev);
25778         
25779         if(!el || el.dom == document){
25780             return;
25781         }
25782         
25783         var bindEl = el;
25784         
25785         // you can not look for children, as if el is the body.. then everythign is the child..
25786         if (!el.attr('tooltip')) { //
25787             if (!el.select("[tooltip]").elements.length) {
25788                 return;
25789             }
25790             // is the mouse over this child...?
25791             bindEl = el.select("[tooltip]").first();
25792             var xy = ev.getXY();
25793             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25794                 //Roo.log("not in region.");
25795                 return;
25796             }
25797             //Roo.log("child element over..");
25798             
25799         }
25800         this.currentEl = bindEl;
25801         this.currentTip.bind(bindEl);
25802         this.currentRegion = Roo.lib.Region.getRegion(dom);
25803         this.currentTip.enter();
25804         
25805     },
25806     leave : function(ev)
25807     {
25808         var dom = ev.getTarget();
25809         //Roo.log(['leave',dom]);
25810         if (!this.currentEl) {
25811             return;
25812         }
25813         
25814         
25815         if (dom != this.currentEl.dom) {
25816             return;
25817         }
25818         var xy = ev.getXY();
25819         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25820             return;
25821         }
25822         // only activate leave if mouse cursor is outside... bounding box..
25823         
25824         
25825         
25826         
25827         if (this.currentTip) {
25828             this.currentTip.leave();
25829         }
25830         //Roo.log('clear currentEl');
25831         this.currentEl = false;
25832         
25833         
25834     },
25835     alignment : {
25836         'left' : ['r-l', [-2,0], 'right'],
25837         'right' : ['l-r', [2,0], 'left'],
25838         'bottom' : ['t-b', [0,2], 'top'],
25839         'top' : [ 'b-t', [0,-2], 'bottom']
25840     }
25841     
25842 });
25843
25844
25845 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25846     
25847     
25848     bindEl : false,
25849     
25850     delay : null, // can be { show : 300 , hide: 500}
25851     
25852     timeout : null,
25853     
25854     hoverState : null, //???
25855     
25856     placement : 'bottom', 
25857     
25858     alignment : false,
25859     
25860     getAutoCreate : function(){
25861     
25862         var cfg = {
25863            cls : 'tooltip',
25864            role : 'tooltip',
25865            cn : [
25866                 {
25867                     cls : 'tooltip-arrow'
25868                 },
25869                 {
25870                     cls : 'tooltip-inner'
25871                 }
25872            ]
25873         };
25874         
25875         return cfg;
25876     },
25877     bind : function(el)
25878     {
25879         this.bindEl = el;
25880     },
25881       
25882     
25883     enter : function () {
25884        
25885         if (this.timeout != null) {
25886             clearTimeout(this.timeout);
25887         }
25888         
25889         this.hoverState = 'in';
25890          //Roo.log("enter - show");
25891         if (!this.delay || !this.delay.show) {
25892             this.show();
25893             return;
25894         }
25895         var _t = this;
25896         this.timeout = setTimeout(function () {
25897             if (_t.hoverState == 'in') {
25898                 _t.show();
25899             }
25900         }, this.delay.show);
25901     },
25902     leave : function()
25903     {
25904         clearTimeout(this.timeout);
25905     
25906         this.hoverState = 'out';
25907          if (!this.delay || !this.delay.hide) {
25908             this.hide();
25909             return;
25910         }
25911        
25912         var _t = this;
25913         this.timeout = setTimeout(function () {
25914             //Roo.log("leave - timeout");
25915             
25916             if (_t.hoverState == 'out') {
25917                 _t.hide();
25918                 Roo.bootstrap.Tooltip.currentEl = false;
25919             }
25920         }, delay);
25921     },
25922     
25923     show : function (msg)
25924     {
25925         if (!this.el) {
25926             this.render(document.body);
25927         }
25928         // set content.
25929         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25930         
25931         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25932         
25933         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25934         
25935         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25936         
25937         var placement = typeof this.placement == 'function' ?
25938             this.placement.call(this, this.el, on_el) :
25939             this.placement;
25940             
25941         var autoToken = /\s?auto?\s?/i;
25942         var autoPlace = autoToken.test(placement);
25943         if (autoPlace) {
25944             placement = placement.replace(autoToken, '') || 'top';
25945         }
25946         
25947         //this.el.detach()
25948         //this.el.setXY([0,0]);
25949         this.el.show();
25950         //this.el.dom.style.display='block';
25951         
25952         //this.el.appendTo(on_el);
25953         
25954         var p = this.getPosition();
25955         var box = this.el.getBox();
25956         
25957         if (autoPlace) {
25958             // fixme..
25959         }
25960         
25961         var align = this.alignment[placement];
25962         
25963         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25964         
25965         if(placement == 'top' || placement == 'bottom'){
25966             if(xy[0] < 0){
25967                 placement = 'right';
25968             }
25969             
25970             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25971                 placement = 'left';
25972             }
25973             
25974             var scroll = Roo.select('body', true).first().getScroll();
25975             
25976             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25977                 placement = 'top';
25978             }
25979             
25980         }
25981         
25982         this.el.alignTo(this.bindEl, align[0],align[1]);
25983         //var arrow = this.el.select('.arrow',true).first();
25984         //arrow.set(align[2], 
25985         
25986         this.el.addClass(placement);
25987         
25988         this.el.addClass('in fade');
25989         
25990         this.hoverState = null;
25991         
25992         if (this.el.hasClass('fade')) {
25993             // fade it?
25994         }
25995         
25996     },
25997     hide : function()
25998     {
25999          
26000         if (!this.el) {
26001             return;
26002         }
26003         //this.el.setXY([0,0]);
26004         this.el.removeClass('in');
26005         //this.el.hide();
26006         
26007     }
26008     
26009 });
26010  
26011
26012  /*
26013  * - LGPL
26014  *
26015  * Location Picker
26016  * 
26017  */
26018
26019 /**
26020  * @class Roo.bootstrap.LocationPicker
26021  * @extends Roo.bootstrap.Component
26022  * Bootstrap LocationPicker class
26023  * @cfg {Number} latitude Position when init default 0
26024  * @cfg {Number} longitude Position when init default 0
26025  * @cfg {Number} zoom default 15
26026  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26027  * @cfg {Boolean} mapTypeControl default false
26028  * @cfg {Boolean} disableDoubleClickZoom default false
26029  * @cfg {Boolean} scrollwheel default true
26030  * @cfg {Boolean} streetViewControl default false
26031  * @cfg {Number} radius default 0
26032  * @cfg {String} locationName
26033  * @cfg {Boolean} draggable default true
26034  * @cfg {Boolean} enableAutocomplete default false
26035  * @cfg {Boolean} enableReverseGeocode default true
26036  * @cfg {String} markerTitle
26037  * 
26038  * @constructor
26039  * Create a new LocationPicker
26040  * @param {Object} config The config object
26041  */
26042
26043
26044 Roo.bootstrap.LocationPicker = function(config){
26045     
26046     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26047     
26048     this.addEvents({
26049         /**
26050          * @event initial
26051          * Fires when the picker initialized.
26052          * @param {Roo.bootstrap.LocationPicker} this
26053          * @param {Google Location} location
26054          */
26055         initial : true,
26056         /**
26057          * @event positionchanged
26058          * Fires when the picker position changed.
26059          * @param {Roo.bootstrap.LocationPicker} this
26060          * @param {Google Location} location
26061          */
26062         positionchanged : true,
26063         /**
26064          * @event resize
26065          * Fires when the map resize.
26066          * @param {Roo.bootstrap.LocationPicker} this
26067          */
26068         resize : true,
26069         /**
26070          * @event show
26071          * Fires when the map show.
26072          * @param {Roo.bootstrap.LocationPicker} this
26073          */
26074         show : true,
26075         /**
26076          * @event hide
26077          * Fires when the map hide.
26078          * @param {Roo.bootstrap.LocationPicker} this
26079          */
26080         hide : true,
26081         /**
26082          * @event mapClick
26083          * Fires when click the map.
26084          * @param {Roo.bootstrap.LocationPicker} this
26085          * @param {Map event} e
26086          */
26087         mapClick : true,
26088         /**
26089          * @event mapRightClick
26090          * Fires when right click the map.
26091          * @param {Roo.bootstrap.LocationPicker} this
26092          * @param {Map event} e
26093          */
26094         mapRightClick : true,
26095         /**
26096          * @event markerClick
26097          * Fires when click the marker.
26098          * @param {Roo.bootstrap.LocationPicker} this
26099          * @param {Map event} e
26100          */
26101         markerClick : true,
26102         /**
26103          * @event markerRightClick
26104          * Fires when right click the marker.
26105          * @param {Roo.bootstrap.LocationPicker} this
26106          * @param {Map event} e
26107          */
26108         markerRightClick : true,
26109         /**
26110          * @event OverlayViewDraw
26111          * Fires when OverlayView Draw
26112          * @param {Roo.bootstrap.LocationPicker} this
26113          */
26114         OverlayViewDraw : true,
26115         /**
26116          * @event OverlayViewOnAdd
26117          * Fires when OverlayView Draw
26118          * @param {Roo.bootstrap.LocationPicker} this
26119          */
26120         OverlayViewOnAdd : true,
26121         /**
26122          * @event OverlayViewOnRemove
26123          * Fires when OverlayView Draw
26124          * @param {Roo.bootstrap.LocationPicker} this
26125          */
26126         OverlayViewOnRemove : true,
26127         /**
26128          * @event OverlayViewShow
26129          * Fires when OverlayView Draw
26130          * @param {Roo.bootstrap.LocationPicker} this
26131          * @param {Pixel} cpx
26132          */
26133         OverlayViewShow : true,
26134         /**
26135          * @event OverlayViewHide
26136          * Fires when OverlayView Draw
26137          * @param {Roo.bootstrap.LocationPicker} this
26138          */
26139         OverlayViewHide : true,
26140         /**
26141          * @event loadexception
26142          * Fires when load google lib failed.
26143          * @param {Roo.bootstrap.LocationPicker} this
26144          */
26145         loadexception : true
26146     });
26147         
26148 };
26149
26150 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26151     
26152     gMapContext: false,
26153     
26154     latitude: 0,
26155     longitude: 0,
26156     zoom: 15,
26157     mapTypeId: false,
26158     mapTypeControl: false,
26159     disableDoubleClickZoom: false,
26160     scrollwheel: true,
26161     streetViewControl: false,
26162     radius: 0,
26163     locationName: '',
26164     draggable: true,
26165     enableAutocomplete: false,
26166     enableReverseGeocode: true,
26167     markerTitle: '',
26168     
26169     getAutoCreate: function()
26170     {
26171
26172         var cfg = {
26173             tag: 'div',
26174             cls: 'roo-location-picker'
26175         };
26176         
26177         return cfg
26178     },
26179     
26180     initEvents: function(ct, position)
26181     {       
26182         if(!this.el.getWidth() || this.isApplied()){
26183             return;
26184         }
26185         
26186         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26187         
26188         this.initial();
26189     },
26190     
26191     initial: function()
26192     {
26193         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26194             this.fireEvent('loadexception', this);
26195             return;
26196         }
26197         
26198         if(!this.mapTypeId){
26199             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26200         }
26201         
26202         this.gMapContext = this.GMapContext();
26203         
26204         this.initOverlayView();
26205         
26206         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26207         
26208         var _this = this;
26209                 
26210         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26211             _this.setPosition(_this.gMapContext.marker.position);
26212         });
26213         
26214         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26215             _this.fireEvent('mapClick', this, event);
26216             
26217         });
26218
26219         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26220             _this.fireEvent('mapRightClick', this, event);
26221             
26222         });
26223         
26224         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26225             _this.fireEvent('markerClick', this, event);
26226             
26227         });
26228
26229         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26230             _this.fireEvent('markerRightClick', this, event);
26231             
26232         });
26233         
26234         this.setPosition(this.gMapContext.location);
26235         
26236         this.fireEvent('initial', this, this.gMapContext.location);
26237     },
26238     
26239     initOverlayView: function()
26240     {
26241         var _this = this;
26242         
26243         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26244             
26245             draw: function()
26246             {
26247                 _this.fireEvent('OverlayViewDraw', _this);
26248             },
26249             
26250             onAdd: function()
26251             {
26252                 _this.fireEvent('OverlayViewOnAdd', _this);
26253             },
26254             
26255             onRemove: function()
26256             {
26257                 _this.fireEvent('OverlayViewOnRemove', _this);
26258             },
26259             
26260             show: function(cpx)
26261             {
26262                 _this.fireEvent('OverlayViewShow', _this, cpx);
26263             },
26264             
26265             hide: function()
26266             {
26267                 _this.fireEvent('OverlayViewHide', _this);
26268             }
26269             
26270         });
26271     },
26272     
26273     fromLatLngToContainerPixel: function(event)
26274     {
26275         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26276     },
26277     
26278     isApplied: function() 
26279     {
26280         return this.getGmapContext() == false ? false : true;
26281     },
26282     
26283     getGmapContext: function() 
26284     {
26285         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26286     },
26287     
26288     GMapContext: function() 
26289     {
26290         var position = new google.maps.LatLng(this.latitude, this.longitude);
26291         
26292         var _map = new google.maps.Map(this.el.dom, {
26293             center: position,
26294             zoom: this.zoom,
26295             mapTypeId: this.mapTypeId,
26296             mapTypeControl: this.mapTypeControl,
26297             disableDoubleClickZoom: this.disableDoubleClickZoom,
26298             scrollwheel: this.scrollwheel,
26299             streetViewControl: this.streetViewControl,
26300             locationName: this.locationName,
26301             draggable: this.draggable,
26302             enableAutocomplete: this.enableAutocomplete,
26303             enableReverseGeocode: this.enableReverseGeocode
26304         });
26305         
26306         var _marker = new google.maps.Marker({
26307             position: position,
26308             map: _map,
26309             title: this.markerTitle,
26310             draggable: this.draggable
26311         });
26312         
26313         return {
26314             map: _map,
26315             marker: _marker,
26316             circle: null,
26317             location: position,
26318             radius: this.radius,
26319             locationName: this.locationName,
26320             addressComponents: {
26321                 formatted_address: null,
26322                 addressLine1: null,
26323                 addressLine2: null,
26324                 streetName: null,
26325                 streetNumber: null,
26326                 city: null,
26327                 district: null,
26328                 state: null,
26329                 stateOrProvince: null
26330             },
26331             settings: this,
26332             domContainer: this.el.dom,
26333             geodecoder: new google.maps.Geocoder()
26334         };
26335     },
26336     
26337     drawCircle: function(center, radius, options) 
26338     {
26339         if (this.gMapContext.circle != null) {
26340             this.gMapContext.circle.setMap(null);
26341         }
26342         if (radius > 0) {
26343             radius *= 1;
26344             options = Roo.apply({}, options, {
26345                 strokeColor: "#0000FF",
26346                 strokeOpacity: .35,
26347                 strokeWeight: 2,
26348                 fillColor: "#0000FF",
26349                 fillOpacity: .2
26350             });
26351             
26352             options.map = this.gMapContext.map;
26353             options.radius = radius;
26354             options.center = center;
26355             this.gMapContext.circle = new google.maps.Circle(options);
26356             return this.gMapContext.circle;
26357         }
26358         
26359         return null;
26360     },
26361     
26362     setPosition: function(location) 
26363     {
26364         this.gMapContext.location = location;
26365         this.gMapContext.marker.setPosition(location);
26366         this.gMapContext.map.panTo(location);
26367         this.drawCircle(location, this.gMapContext.radius, {});
26368         
26369         var _this = this;
26370         
26371         if (this.gMapContext.settings.enableReverseGeocode) {
26372             this.gMapContext.geodecoder.geocode({
26373                 latLng: this.gMapContext.location
26374             }, function(results, status) {
26375                 
26376                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26377                     _this.gMapContext.locationName = results[0].formatted_address;
26378                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26379                     
26380                     _this.fireEvent('positionchanged', this, location);
26381                 }
26382             });
26383             
26384             return;
26385         }
26386         
26387         this.fireEvent('positionchanged', this, location);
26388     },
26389     
26390     resize: function()
26391     {
26392         google.maps.event.trigger(this.gMapContext.map, "resize");
26393         
26394         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26395         
26396         this.fireEvent('resize', this);
26397     },
26398     
26399     setPositionByLatLng: function(latitude, longitude)
26400     {
26401         this.setPosition(new google.maps.LatLng(latitude, longitude));
26402     },
26403     
26404     getCurrentPosition: function() 
26405     {
26406         return {
26407             latitude: this.gMapContext.location.lat(),
26408             longitude: this.gMapContext.location.lng()
26409         };
26410     },
26411     
26412     getAddressName: function() 
26413     {
26414         return this.gMapContext.locationName;
26415     },
26416     
26417     getAddressComponents: function() 
26418     {
26419         return this.gMapContext.addressComponents;
26420     },
26421     
26422     address_component_from_google_geocode: function(address_components) 
26423     {
26424         var result = {};
26425         
26426         for (var i = 0; i < address_components.length; i++) {
26427             var component = address_components[i];
26428             if (component.types.indexOf("postal_code") >= 0) {
26429                 result.postalCode = component.short_name;
26430             } else if (component.types.indexOf("street_number") >= 0) {
26431                 result.streetNumber = component.short_name;
26432             } else if (component.types.indexOf("route") >= 0) {
26433                 result.streetName = component.short_name;
26434             } else if (component.types.indexOf("neighborhood") >= 0) {
26435                 result.city = component.short_name;
26436             } else if (component.types.indexOf("locality") >= 0) {
26437                 result.city = component.short_name;
26438             } else if (component.types.indexOf("sublocality") >= 0) {
26439                 result.district = component.short_name;
26440             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26441                 result.stateOrProvince = component.short_name;
26442             } else if (component.types.indexOf("country") >= 0) {
26443                 result.country = component.short_name;
26444             }
26445         }
26446         
26447         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26448         result.addressLine2 = "";
26449         return result;
26450     },
26451     
26452     setZoomLevel: function(zoom)
26453     {
26454         this.gMapContext.map.setZoom(zoom);
26455     },
26456     
26457     show: function()
26458     {
26459         if(!this.el){
26460             return;
26461         }
26462         
26463         this.el.show();
26464         
26465         this.resize();
26466         
26467         this.fireEvent('show', this);
26468     },
26469     
26470     hide: function()
26471     {
26472         if(!this.el){
26473             return;
26474         }
26475         
26476         this.el.hide();
26477         
26478         this.fireEvent('hide', this);
26479     }
26480     
26481 });
26482
26483 Roo.apply(Roo.bootstrap.LocationPicker, {
26484     
26485     OverlayView : function(map, options)
26486     {
26487         options = options || {};
26488         
26489         this.setMap(map);
26490     }
26491     
26492     
26493 });/*
26494  * - LGPL
26495  *
26496  * Alert
26497  * 
26498  */
26499
26500 /**
26501  * @class Roo.bootstrap.Alert
26502  * @extends Roo.bootstrap.Component
26503  * Bootstrap Alert class
26504  * @cfg {String} title The title of alert
26505  * @cfg {String} html The content of alert
26506  * @cfg {String} weight (  success | info | warning | danger )
26507  * @cfg {String} faicon font-awesomeicon
26508  * 
26509  * @constructor
26510  * Create a new alert
26511  * @param {Object} config The config object
26512  */
26513
26514
26515 Roo.bootstrap.Alert = function(config){
26516     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26517     
26518 };
26519
26520 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26521     
26522     title: '',
26523     html: '',
26524     weight: false,
26525     faicon: false,
26526     
26527     getAutoCreate : function()
26528     {
26529         
26530         var cfg = {
26531             tag : 'div',
26532             cls : 'alert',
26533             cn : [
26534                 {
26535                     tag : 'i',
26536                     cls : 'roo-alert-icon'
26537                     
26538                 },
26539                 {
26540                     tag : 'b',
26541                     cls : 'roo-alert-title',
26542                     html : this.title
26543                 },
26544                 {
26545                     tag : 'span',
26546                     cls : 'roo-alert-text',
26547                     html : this.html
26548                 }
26549             ]
26550         };
26551         
26552         if(this.faicon){
26553             cfg.cn[0].cls += ' fa ' + this.faicon;
26554         }
26555         
26556         if(this.weight){
26557             cfg.cls += ' alert-' + this.weight;
26558         }
26559         
26560         return cfg;
26561     },
26562     
26563     initEvents: function() 
26564     {
26565         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26566     },
26567     
26568     setTitle : function(str)
26569     {
26570         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26571     },
26572     
26573     setText : function(str)
26574     {
26575         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26576     },
26577     
26578     setWeight : function(weight)
26579     {
26580         if(this.weight){
26581             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26582         }
26583         
26584         this.weight = weight;
26585         
26586         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26587     },
26588     
26589     setIcon : function(icon)
26590     {
26591         if(this.faicon){
26592             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26593         }
26594         
26595         this.faicon = icon;
26596         
26597         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26598     },
26599     
26600     hide: function() 
26601     {
26602         this.el.hide();   
26603     },
26604     
26605     show: function() 
26606     {  
26607         this.el.show();   
26608     }
26609     
26610 });
26611
26612  
26613 /*
26614 * Licence: LGPL
26615 */
26616
26617 /**
26618  * @class Roo.bootstrap.UploadCropbox
26619  * @extends Roo.bootstrap.Component
26620  * Bootstrap UploadCropbox class
26621  * @cfg {String} emptyText show when image has been loaded
26622  * @cfg {String} rotateNotify show when image too small to rotate
26623  * @cfg {Number} errorTimeout default 3000
26624  * @cfg {Number} minWidth default 300
26625  * @cfg {Number} minHeight default 300
26626  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26627  * @cfg {Boolean} isDocument (true|false) default false
26628  * @cfg {String} url action url
26629  * @cfg {String} paramName default 'imageUpload'
26630  * @cfg {String} method default POST
26631  * @cfg {Boolean} loadMask (true|false) default true
26632  * @cfg {Boolean} loadingText default 'Loading...'
26633  * 
26634  * @constructor
26635  * Create a new UploadCropbox
26636  * @param {Object} config The config object
26637  */
26638
26639 Roo.bootstrap.UploadCropbox = function(config){
26640     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26641     
26642     this.addEvents({
26643         /**
26644          * @event beforeselectfile
26645          * Fire before select file
26646          * @param {Roo.bootstrap.UploadCropbox} this
26647          */
26648         "beforeselectfile" : true,
26649         /**
26650          * @event initial
26651          * Fire after initEvent
26652          * @param {Roo.bootstrap.UploadCropbox} this
26653          */
26654         "initial" : true,
26655         /**
26656          * @event crop
26657          * Fire after initEvent
26658          * @param {Roo.bootstrap.UploadCropbox} this
26659          * @param {String} data
26660          */
26661         "crop" : true,
26662         /**
26663          * @event prepare
26664          * Fire when preparing the file data
26665          * @param {Roo.bootstrap.UploadCropbox} this
26666          * @param {Object} file
26667          */
26668         "prepare" : true,
26669         /**
26670          * @event exception
26671          * Fire when get exception
26672          * @param {Roo.bootstrap.UploadCropbox} this
26673          * @param {XMLHttpRequest} xhr
26674          */
26675         "exception" : true,
26676         /**
26677          * @event beforeloadcanvas
26678          * Fire before load the canvas
26679          * @param {Roo.bootstrap.UploadCropbox} this
26680          * @param {String} src
26681          */
26682         "beforeloadcanvas" : true,
26683         /**
26684          * @event trash
26685          * Fire when trash image
26686          * @param {Roo.bootstrap.UploadCropbox} this
26687          */
26688         "trash" : true,
26689         /**
26690          * @event download
26691          * Fire when download the image
26692          * @param {Roo.bootstrap.UploadCropbox} this
26693          */
26694         "download" : true,
26695         /**
26696          * @event footerbuttonclick
26697          * Fire when footerbuttonclick
26698          * @param {Roo.bootstrap.UploadCropbox} this
26699          * @param {String} type
26700          */
26701         "footerbuttonclick" : true,
26702         /**
26703          * @event resize
26704          * Fire when resize
26705          * @param {Roo.bootstrap.UploadCropbox} this
26706          */
26707         "resize" : true,
26708         /**
26709          * @event rotate
26710          * Fire when rotate the image
26711          * @param {Roo.bootstrap.UploadCropbox} this
26712          * @param {String} pos
26713          */
26714         "rotate" : true,
26715         /**
26716          * @event inspect
26717          * Fire when inspect the file
26718          * @param {Roo.bootstrap.UploadCropbox} this
26719          * @param {Object} file
26720          */
26721         "inspect" : true,
26722         /**
26723          * @event upload
26724          * Fire when xhr upload the file
26725          * @param {Roo.bootstrap.UploadCropbox} this
26726          * @param {Object} data
26727          */
26728         "upload" : true,
26729         /**
26730          * @event arrange
26731          * Fire when arrange the file data
26732          * @param {Roo.bootstrap.UploadCropbox} this
26733          * @param {Object} formData
26734          */
26735         "arrange" : true
26736     });
26737     
26738     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26739 };
26740
26741 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26742     
26743     emptyText : 'Click to upload image',
26744     rotateNotify : 'Image is too small to rotate',
26745     errorTimeout : 3000,
26746     scale : 0,
26747     baseScale : 1,
26748     rotate : 0,
26749     dragable : false,
26750     pinching : false,
26751     mouseX : 0,
26752     mouseY : 0,
26753     cropData : false,
26754     minWidth : 300,
26755     minHeight : 300,
26756     file : false,
26757     exif : {},
26758     baseRotate : 1,
26759     cropType : 'image/jpeg',
26760     buttons : false,
26761     canvasLoaded : false,
26762     isDocument : false,
26763     method : 'POST',
26764     paramName : 'imageUpload',
26765     loadMask : true,
26766     loadingText : 'Loading...',
26767     maskEl : false,
26768     
26769     getAutoCreate : function()
26770     {
26771         var cfg = {
26772             tag : 'div',
26773             cls : 'roo-upload-cropbox',
26774             cn : [
26775                 {
26776                     tag : 'input',
26777                     cls : 'roo-upload-cropbox-selector',
26778                     type : 'file'
26779                 },
26780                 {
26781                     tag : 'div',
26782                     cls : 'roo-upload-cropbox-body',
26783                     style : 'cursor:pointer',
26784                     cn : [
26785                         {
26786                             tag : 'div',
26787                             cls : 'roo-upload-cropbox-preview'
26788                         },
26789                         {
26790                             tag : 'div',
26791                             cls : 'roo-upload-cropbox-thumb'
26792                         },
26793                         {
26794                             tag : 'div',
26795                             cls : 'roo-upload-cropbox-empty-notify',
26796                             html : this.emptyText
26797                         },
26798                         {
26799                             tag : 'div',
26800                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26801                             html : this.rotateNotify
26802                         }
26803                     ]
26804                 },
26805                 {
26806                     tag : 'div',
26807                     cls : 'roo-upload-cropbox-footer',
26808                     cn : {
26809                         tag : 'div',
26810                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26811                         cn : []
26812                     }
26813                 }
26814             ]
26815         };
26816         
26817         return cfg;
26818     },
26819     
26820     onRender : function(ct, position)
26821     {
26822         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26823         
26824         if (this.buttons.length) {
26825             
26826             Roo.each(this.buttons, function(bb) {
26827                 
26828                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26829                 
26830                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26831                 
26832             }, this);
26833         }
26834         
26835         if(this.loadMask){
26836             this.maskEl = this.el;
26837         }
26838     },
26839     
26840     initEvents : function()
26841     {
26842         this.urlAPI = (window.createObjectURL && window) || 
26843                                 (window.URL && URL.revokeObjectURL && URL) || 
26844                                 (window.webkitURL && webkitURL);
26845                         
26846         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26847         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26848         
26849         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26850         this.selectorEl.hide();
26851         
26852         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26853         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26854         
26855         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26856         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26857         this.thumbEl.hide();
26858         
26859         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26860         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26861         
26862         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26863         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26864         this.errorEl.hide();
26865         
26866         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26867         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26868         this.footerEl.hide();
26869         
26870         this.setThumbBoxSize();
26871         
26872         this.bind();
26873         
26874         this.resize();
26875         
26876         this.fireEvent('initial', this);
26877     },
26878
26879     bind : function()
26880     {
26881         var _this = this;
26882         
26883         window.addEventListener("resize", function() { _this.resize(); } );
26884         
26885         this.bodyEl.on('click', this.beforeSelectFile, this);
26886         
26887         if(Roo.isTouch){
26888             this.bodyEl.on('touchstart', this.onTouchStart, this);
26889             this.bodyEl.on('touchmove', this.onTouchMove, this);
26890             this.bodyEl.on('touchend', this.onTouchEnd, this);
26891         }
26892         
26893         if(!Roo.isTouch){
26894             this.bodyEl.on('mousedown', this.onMouseDown, this);
26895             this.bodyEl.on('mousemove', this.onMouseMove, this);
26896             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26897             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26898             Roo.get(document).on('mouseup', this.onMouseUp, this);
26899         }
26900         
26901         this.selectorEl.on('change', this.onFileSelected, this);
26902     },
26903     
26904     reset : function()
26905     {    
26906         this.scale = 0;
26907         this.baseScale = 1;
26908         this.rotate = 0;
26909         this.baseRotate = 1;
26910         this.dragable = false;
26911         this.pinching = false;
26912         this.mouseX = 0;
26913         this.mouseY = 0;
26914         this.cropData = false;
26915         this.notifyEl.dom.innerHTML = this.emptyText;
26916         
26917         this.selectorEl.dom.value = '';
26918         
26919     },
26920     
26921     resize : function()
26922     {
26923         if(this.fireEvent('resize', this) != false){
26924             this.setThumbBoxPosition();
26925             this.setCanvasPosition();
26926         }
26927     },
26928     
26929     onFooterButtonClick : function(e, el, o, type)
26930     {
26931         switch (type) {
26932             case 'rotate-left' :
26933                 this.onRotateLeft(e);
26934                 break;
26935             case 'rotate-right' :
26936                 this.onRotateRight(e);
26937                 break;
26938             case 'picture' :
26939                 this.beforeSelectFile(e);
26940                 break;
26941             case 'trash' :
26942                 this.trash(e);
26943                 break;
26944             case 'crop' :
26945                 this.crop(e);
26946                 break;
26947             case 'download' :
26948                 this.download(e);
26949                 break;
26950             default :
26951                 break;
26952         }
26953         
26954         this.fireEvent('footerbuttonclick', this, type);
26955     },
26956     
26957     beforeSelectFile : function(e)
26958     {
26959         e.preventDefault();
26960         
26961         if(this.fireEvent('beforeselectfile', this) != false){
26962             this.selectorEl.dom.click();
26963         }
26964     },
26965     
26966     onFileSelected : function(e)
26967     {
26968         e.preventDefault();
26969         
26970         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26971             return;
26972         }
26973         
26974         var file = this.selectorEl.dom.files[0];
26975         
26976         if(this.fireEvent('inspect', this, file) != false){
26977             this.prepare(file);
26978         }
26979         
26980     },
26981     
26982     trash : function(e)
26983     {
26984         this.fireEvent('trash', this);
26985     },
26986     
26987     download : function(e)
26988     {
26989         this.fireEvent('download', this);
26990     },
26991     
26992     loadCanvas : function(src)
26993     {   
26994         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26995             
26996             this.reset();
26997             
26998             this.imageEl = document.createElement('img');
26999             
27000             var _this = this;
27001             
27002             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27003             
27004             this.imageEl.src = src;
27005         }
27006     },
27007     
27008     onLoadCanvas : function()
27009     {   
27010         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27011         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27012         
27013         this.bodyEl.un('click', this.beforeSelectFile, this);
27014         
27015         this.notifyEl.hide();
27016         this.thumbEl.show();
27017         this.footerEl.show();
27018         
27019         this.baseRotateLevel();
27020         
27021         if(this.isDocument){
27022             this.setThumbBoxSize();
27023         }
27024         
27025         this.setThumbBoxPosition();
27026         
27027         this.baseScaleLevel();
27028         
27029         this.draw();
27030         
27031         this.resize();
27032         
27033         this.canvasLoaded = true;
27034         
27035         if(this.loadMask){
27036             this.maskEl.unmask();
27037         }
27038         
27039     },
27040     
27041     setCanvasPosition : function()
27042     {   
27043         if(!this.canvasEl){
27044             return;
27045         }
27046         
27047         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27048         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27049         
27050         this.previewEl.setLeft(pw);
27051         this.previewEl.setTop(ph);
27052         
27053     },
27054     
27055     onMouseDown : function(e)
27056     {   
27057         e.stopEvent();
27058         
27059         this.dragable = true;
27060         this.pinching = false;
27061         
27062         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27063             this.dragable = false;
27064             return;
27065         }
27066         
27067         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27068         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27069         
27070     },
27071     
27072     onMouseMove : function(e)
27073     {   
27074         e.stopEvent();
27075         
27076         if(!this.canvasLoaded){
27077             return;
27078         }
27079         
27080         if (!this.dragable){
27081             return;
27082         }
27083         
27084         var minX = Math.ceil(this.thumbEl.getLeft(true));
27085         var minY = Math.ceil(this.thumbEl.getTop(true));
27086         
27087         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27088         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27089         
27090         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27091         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27092         
27093         x = x - this.mouseX;
27094         y = y - this.mouseY;
27095         
27096         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27097         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27098         
27099         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27100         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27101         
27102         this.previewEl.setLeft(bgX);
27103         this.previewEl.setTop(bgY);
27104         
27105         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27106         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27107     },
27108     
27109     onMouseUp : function(e)
27110     {   
27111         e.stopEvent();
27112         
27113         this.dragable = false;
27114     },
27115     
27116     onMouseWheel : function(e)
27117     {   
27118         e.stopEvent();
27119         
27120         this.startScale = this.scale;
27121         
27122         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27123         
27124         if(!this.zoomable()){
27125             this.scale = this.startScale;
27126             return;
27127         }
27128         
27129         this.draw();
27130         
27131         return;
27132     },
27133     
27134     zoomable : function()
27135     {
27136         var minScale = this.thumbEl.getWidth() / this.minWidth;
27137         
27138         if(this.minWidth < this.minHeight){
27139             minScale = this.thumbEl.getHeight() / this.minHeight;
27140         }
27141         
27142         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27143         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27144         
27145         if(
27146                 this.isDocument &&
27147                 (this.rotate == 0 || this.rotate == 180) && 
27148                 (
27149                     width > this.imageEl.OriginWidth || 
27150                     height > this.imageEl.OriginHeight ||
27151                     (width < this.minWidth && height < this.minHeight)
27152                 )
27153         ){
27154             return false;
27155         }
27156         
27157         if(
27158                 this.isDocument &&
27159                 (this.rotate == 90 || this.rotate == 270) && 
27160                 (
27161                     width > this.imageEl.OriginWidth || 
27162                     height > this.imageEl.OriginHeight ||
27163                     (width < this.minHeight && height < this.minWidth)
27164                 )
27165         ){
27166             return false;
27167         }
27168         
27169         if(
27170                 !this.isDocument &&
27171                 (this.rotate == 0 || this.rotate == 180) && 
27172                 (
27173                     width < this.minWidth || 
27174                     width > this.imageEl.OriginWidth || 
27175                     height < this.minHeight || 
27176                     height > this.imageEl.OriginHeight
27177                 )
27178         ){
27179             return false;
27180         }
27181         
27182         if(
27183                 !this.isDocument &&
27184                 (this.rotate == 90 || this.rotate == 270) && 
27185                 (
27186                     width < this.minHeight || 
27187                     width > this.imageEl.OriginWidth || 
27188                     height < this.minWidth || 
27189                     height > this.imageEl.OriginHeight
27190                 )
27191         ){
27192             return false;
27193         }
27194         
27195         return true;
27196         
27197     },
27198     
27199     onRotateLeft : function(e)
27200     {   
27201         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27202             
27203             var minScale = this.thumbEl.getWidth() / this.minWidth;
27204             
27205             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27206             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27207             
27208             this.startScale = this.scale;
27209             
27210             while (this.getScaleLevel() < minScale){
27211             
27212                 this.scale = this.scale + 1;
27213                 
27214                 if(!this.zoomable()){
27215                     break;
27216                 }
27217                 
27218                 if(
27219                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27220                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27221                 ){
27222                     continue;
27223                 }
27224                 
27225                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27226
27227                 this.draw();
27228                 
27229                 return;
27230             }
27231             
27232             this.scale = this.startScale;
27233             
27234             this.onRotateFail();
27235             
27236             return false;
27237         }
27238         
27239         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27240
27241         if(this.isDocument){
27242             this.setThumbBoxSize();
27243             this.setThumbBoxPosition();
27244             this.setCanvasPosition();
27245         }
27246         
27247         this.draw();
27248         
27249         this.fireEvent('rotate', this, 'left');
27250         
27251     },
27252     
27253     onRotateRight : function(e)
27254     {
27255         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27256             
27257             var minScale = this.thumbEl.getWidth() / this.minWidth;
27258         
27259             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27260             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27261             
27262             this.startScale = this.scale;
27263             
27264             while (this.getScaleLevel() < minScale){
27265             
27266                 this.scale = this.scale + 1;
27267                 
27268                 if(!this.zoomable()){
27269                     break;
27270                 }
27271                 
27272                 if(
27273                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27274                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27275                 ){
27276                     continue;
27277                 }
27278                 
27279                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27280
27281                 this.draw();
27282                 
27283                 return;
27284             }
27285             
27286             this.scale = this.startScale;
27287             
27288             this.onRotateFail();
27289             
27290             return false;
27291         }
27292         
27293         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27294
27295         if(this.isDocument){
27296             this.setThumbBoxSize();
27297             this.setThumbBoxPosition();
27298             this.setCanvasPosition();
27299         }
27300         
27301         this.draw();
27302         
27303         this.fireEvent('rotate', this, 'right');
27304     },
27305     
27306     onRotateFail : function()
27307     {
27308         this.errorEl.show(true);
27309         
27310         var _this = this;
27311         
27312         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27313     },
27314     
27315     draw : function()
27316     {
27317         this.previewEl.dom.innerHTML = '';
27318         
27319         var canvasEl = document.createElement("canvas");
27320         
27321         var contextEl = canvasEl.getContext("2d");
27322         
27323         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27324         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27325         var center = this.imageEl.OriginWidth / 2;
27326         
27327         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27328             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27329             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27330             center = this.imageEl.OriginHeight / 2;
27331         }
27332         
27333         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27334         
27335         contextEl.translate(center, center);
27336         contextEl.rotate(this.rotate * Math.PI / 180);
27337
27338         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27339         
27340         this.canvasEl = document.createElement("canvas");
27341         
27342         this.contextEl = this.canvasEl.getContext("2d");
27343         
27344         switch (this.rotate) {
27345             case 0 :
27346                 
27347                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27348                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27349                 
27350                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27351                 
27352                 break;
27353             case 90 : 
27354                 
27355                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27356                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27357                 
27358                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27359                     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);
27360                     break;
27361                 }
27362                 
27363                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27364                 
27365                 break;
27366             case 180 :
27367                 
27368                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27369                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27370                 
27371                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27372                     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);
27373                     break;
27374                 }
27375                 
27376                 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);
27377                 
27378                 break;
27379             case 270 :
27380                 
27381                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27382                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27383         
27384                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27385                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27386                     break;
27387                 }
27388                 
27389                 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);
27390                 
27391                 break;
27392             default : 
27393                 break;
27394         }
27395         
27396         this.previewEl.appendChild(this.canvasEl);
27397         
27398         this.setCanvasPosition();
27399     },
27400     
27401     crop : function()
27402     {
27403         if(!this.canvasLoaded){
27404             return;
27405         }
27406         
27407         var imageCanvas = document.createElement("canvas");
27408         
27409         var imageContext = imageCanvas.getContext("2d");
27410         
27411         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27412         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27413         
27414         var center = imageCanvas.width / 2;
27415         
27416         imageContext.translate(center, center);
27417         
27418         imageContext.rotate(this.rotate * Math.PI / 180);
27419         
27420         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27421         
27422         var canvas = document.createElement("canvas");
27423         
27424         var context = canvas.getContext("2d");
27425                 
27426         canvas.width = this.minWidth;
27427         canvas.height = this.minHeight;
27428
27429         switch (this.rotate) {
27430             case 0 :
27431                 
27432                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27433                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27434                 
27435                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27436                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27437                 
27438                 var targetWidth = this.minWidth - 2 * x;
27439                 var targetHeight = this.minHeight - 2 * y;
27440                 
27441                 var scale = 1;
27442                 
27443                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27444                     scale = targetWidth / width;
27445                 }
27446                 
27447                 if(x > 0 && y == 0){
27448                     scale = targetHeight / height;
27449                 }
27450                 
27451                 if(x > 0 && y > 0){
27452                     scale = targetWidth / width;
27453                     
27454                     if(width < height){
27455                         scale = targetHeight / height;
27456                     }
27457                 }
27458                 
27459                 context.scale(scale, scale);
27460                 
27461                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27462                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27463
27464                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27465                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27466
27467                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27468                 
27469                 break;
27470             case 90 : 
27471                 
27472                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27473                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27474                 
27475                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27476                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27477                 
27478                 var targetWidth = this.minWidth - 2 * x;
27479                 var targetHeight = this.minHeight - 2 * y;
27480                 
27481                 var scale = 1;
27482                 
27483                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27484                     scale = targetWidth / width;
27485                 }
27486                 
27487                 if(x > 0 && y == 0){
27488                     scale = targetHeight / height;
27489                 }
27490                 
27491                 if(x > 0 && y > 0){
27492                     scale = targetWidth / width;
27493                     
27494                     if(width < height){
27495                         scale = targetHeight / height;
27496                     }
27497                 }
27498                 
27499                 context.scale(scale, scale);
27500                 
27501                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27502                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27503
27504                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27505                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27506                 
27507                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27508                 
27509                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27510                 
27511                 break;
27512             case 180 :
27513                 
27514                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27515                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27516                 
27517                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27518                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27519                 
27520                 var targetWidth = this.minWidth - 2 * x;
27521                 var targetHeight = this.minHeight - 2 * y;
27522                 
27523                 var scale = 1;
27524                 
27525                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27526                     scale = targetWidth / width;
27527                 }
27528                 
27529                 if(x > 0 && y == 0){
27530                     scale = targetHeight / height;
27531                 }
27532                 
27533                 if(x > 0 && y > 0){
27534                     scale = targetWidth / width;
27535                     
27536                     if(width < height){
27537                         scale = targetHeight / height;
27538                     }
27539                 }
27540                 
27541                 context.scale(scale, scale);
27542                 
27543                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27544                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27545
27546                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27547                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27548
27549                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27550                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27551                 
27552                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27553                 
27554                 break;
27555             case 270 :
27556                 
27557                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27558                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27559                 
27560                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27561                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27562                 
27563                 var targetWidth = this.minWidth - 2 * x;
27564                 var targetHeight = this.minHeight - 2 * y;
27565                 
27566                 var scale = 1;
27567                 
27568                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27569                     scale = targetWidth / width;
27570                 }
27571                 
27572                 if(x > 0 && y == 0){
27573                     scale = targetHeight / height;
27574                 }
27575                 
27576                 if(x > 0 && y > 0){
27577                     scale = targetWidth / width;
27578                     
27579                     if(width < height){
27580                         scale = targetHeight / height;
27581                     }
27582                 }
27583                 
27584                 context.scale(scale, scale);
27585                 
27586                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27587                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27588
27589                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27590                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27591                 
27592                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27593                 
27594                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27595                 
27596                 break;
27597             default : 
27598                 break;
27599         }
27600         
27601         this.cropData = canvas.toDataURL(this.cropType);
27602         
27603         if(this.fireEvent('crop', this, this.cropData) !== false){
27604             this.process(this.file, this.cropData);
27605         }
27606         
27607         return;
27608         
27609     },
27610     
27611     setThumbBoxSize : function()
27612     {
27613         var width, height;
27614         
27615         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27616             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27617             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27618             
27619             this.minWidth = width;
27620             this.minHeight = height;
27621             
27622             if(this.rotate == 90 || this.rotate == 270){
27623                 this.minWidth = height;
27624                 this.minHeight = width;
27625             }
27626         }
27627         
27628         height = 300;
27629         width = Math.ceil(this.minWidth * height / this.minHeight);
27630         
27631         if(this.minWidth > this.minHeight){
27632             width = 300;
27633             height = Math.ceil(this.minHeight * width / this.minWidth);
27634         }
27635         
27636         this.thumbEl.setStyle({
27637             width : width + 'px',
27638             height : height + 'px'
27639         });
27640
27641         return;
27642             
27643     },
27644     
27645     setThumbBoxPosition : function()
27646     {
27647         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27648         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27649         
27650         this.thumbEl.setLeft(x);
27651         this.thumbEl.setTop(y);
27652         
27653     },
27654     
27655     baseRotateLevel : function()
27656     {
27657         this.baseRotate = 1;
27658         
27659         if(
27660                 typeof(this.exif) != 'undefined' &&
27661                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27662                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27663         ){
27664             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27665         }
27666         
27667         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27668         
27669     },
27670     
27671     baseScaleLevel : function()
27672     {
27673         var width, height;
27674         
27675         if(this.isDocument){
27676             
27677             if(this.baseRotate == 6 || this.baseRotate == 8){
27678             
27679                 height = this.thumbEl.getHeight();
27680                 this.baseScale = height / this.imageEl.OriginWidth;
27681
27682                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27683                     width = this.thumbEl.getWidth();
27684                     this.baseScale = width / this.imageEl.OriginHeight;
27685                 }
27686
27687                 return;
27688             }
27689
27690             height = this.thumbEl.getHeight();
27691             this.baseScale = height / this.imageEl.OriginHeight;
27692
27693             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27694                 width = this.thumbEl.getWidth();
27695                 this.baseScale = width / this.imageEl.OriginWidth;
27696             }
27697
27698             return;
27699         }
27700         
27701         if(this.baseRotate == 6 || this.baseRotate == 8){
27702             
27703             width = this.thumbEl.getHeight();
27704             this.baseScale = width / this.imageEl.OriginHeight;
27705             
27706             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27707                 height = this.thumbEl.getWidth();
27708                 this.baseScale = height / this.imageEl.OriginHeight;
27709             }
27710             
27711             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27712                 height = this.thumbEl.getWidth();
27713                 this.baseScale = height / this.imageEl.OriginHeight;
27714                 
27715                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27716                     width = this.thumbEl.getHeight();
27717                     this.baseScale = width / this.imageEl.OriginWidth;
27718                 }
27719             }
27720             
27721             return;
27722         }
27723         
27724         width = this.thumbEl.getWidth();
27725         this.baseScale = width / this.imageEl.OriginWidth;
27726         
27727         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27728             height = this.thumbEl.getHeight();
27729             this.baseScale = height / this.imageEl.OriginHeight;
27730         }
27731         
27732         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27733             
27734             height = this.thumbEl.getHeight();
27735             this.baseScale = height / this.imageEl.OriginHeight;
27736             
27737             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27738                 width = this.thumbEl.getWidth();
27739                 this.baseScale = width / this.imageEl.OriginWidth;
27740             }
27741             
27742         }
27743         
27744         return;
27745     },
27746     
27747     getScaleLevel : function()
27748     {
27749         return this.baseScale * Math.pow(1.1, this.scale);
27750     },
27751     
27752     onTouchStart : function(e)
27753     {
27754         if(!this.canvasLoaded){
27755             this.beforeSelectFile(e);
27756             return;
27757         }
27758         
27759         var touches = e.browserEvent.touches;
27760         
27761         if(!touches){
27762             return;
27763         }
27764         
27765         if(touches.length == 1){
27766             this.onMouseDown(e);
27767             return;
27768         }
27769         
27770         if(touches.length != 2){
27771             return;
27772         }
27773         
27774         var coords = [];
27775         
27776         for(var i = 0, finger; finger = touches[i]; i++){
27777             coords.push(finger.pageX, finger.pageY);
27778         }
27779         
27780         var x = Math.pow(coords[0] - coords[2], 2);
27781         var y = Math.pow(coords[1] - coords[3], 2);
27782         
27783         this.startDistance = Math.sqrt(x + y);
27784         
27785         this.startScale = this.scale;
27786         
27787         this.pinching = true;
27788         this.dragable = false;
27789         
27790     },
27791     
27792     onTouchMove : function(e)
27793     {
27794         if(!this.pinching && !this.dragable){
27795             return;
27796         }
27797         
27798         var touches = e.browserEvent.touches;
27799         
27800         if(!touches){
27801             return;
27802         }
27803         
27804         if(this.dragable){
27805             this.onMouseMove(e);
27806             return;
27807         }
27808         
27809         var coords = [];
27810         
27811         for(var i = 0, finger; finger = touches[i]; i++){
27812             coords.push(finger.pageX, finger.pageY);
27813         }
27814         
27815         var x = Math.pow(coords[0] - coords[2], 2);
27816         var y = Math.pow(coords[1] - coords[3], 2);
27817         
27818         this.endDistance = Math.sqrt(x + y);
27819         
27820         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27821         
27822         if(!this.zoomable()){
27823             this.scale = this.startScale;
27824             return;
27825         }
27826         
27827         this.draw();
27828         
27829     },
27830     
27831     onTouchEnd : function(e)
27832     {
27833         this.pinching = false;
27834         this.dragable = false;
27835         
27836     },
27837     
27838     process : function(file, crop)
27839     {
27840         if(this.loadMask){
27841             this.maskEl.mask(this.loadingText);
27842         }
27843         
27844         this.xhr = new XMLHttpRequest();
27845         
27846         file.xhr = this.xhr;
27847
27848         this.xhr.open(this.method, this.url, true);
27849         
27850         var headers = {
27851             "Accept": "application/json",
27852             "Cache-Control": "no-cache",
27853             "X-Requested-With": "XMLHttpRequest"
27854         };
27855         
27856         for (var headerName in headers) {
27857             var headerValue = headers[headerName];
27858             if (headerValue) {
27859                 this.xhr.setRequestHeader(headerName, headerValue);
27860             }
27861         }
27862         
27863         var _this = this;
27864         
27865         this.xhr.onload = function()
27866         {
27867             _this.xhrOnLoad(_this.xhr);
27868         }
27869         
27870         this.xhr.onerror = function()
27871         {
27872             _this.xhrOnError(_this.xhr);
27873         }
27874         
27875         var formData = new FormData();
27876
27877         formData.append('returnHTML', 'NO');
27878         
27879         if(crop){
27880             formData.append('crop', crop);
27881         }
27882         
27883         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27884             formData.append(this.paramName, file, file.name);
27885         }
27886         
27887         if(typeof(file.filename) != 'undefined'){
27888             formData.append('filename', file.filename);
27889         }
27890         
27891         if(typeof(file.mimetype) != 'undefined'){
27892             formData.append('mimetype', file.mimetype);
27893         }
27894         
27895         if(this.fireEvent('arrange', this, formData) != false){
27896             this.xhr.send(formData);
27897         };
27898     },
27899     
27900     xhrOnLoad : function(xhr)
27901     {
27902         if(this.loadMask){
27903             this.maskEl.unmask();
27904         }
27905         
27906         if (xhr.readyState !== 4) {
27907             this.fireEvent('exception', this, xhr);
27908             return;
27909         }
27910
27911         var response = Roo.decode(xhr.responseText);
27912         
27913         if(!response.success){
27914             this.fireEvent('exception', this, xhr);
27915             return;
27916         }
27917         
27918         var response = Roo.decode(xhr.responseText);
27919         
27920         this.fireEvent('upload', this, response);
27921         
27922     },
27923     
27924     xhrOnError : function()
27925     {
27926         if(this.loadMask){
27927             this.maskEl.unmask();
27928         }
27929         
27930         Roo.log('xhr on error');
27931         
27932         var response = Roo.decode(xhr.responseText);
27933           
27934         Roo.log(response);
27935         
27936     },
27937     
27938     prepare : function(file)
27939     {   
27940         if(this.loadMask){
27941             this.maskEl.mask(this.loadingText);
27942         }
27943         
27944         this.file = false;
27945         this.exif = {};
27946         
27947         if(typeof(file) === 'string'){
27948             this.loadCanvas(file);
27949             return;
27950         }
27951         
27952         if(!file || !this.urlAPI){
27953             return;
27954         }
27955         
27956         this.file = file;
27957         this.cropType = file.type;
27958         
27959         var _this = this;
27960         
27961         if(this.fireEvent('prepare', this, this.file) != false){
27962             
27963             var reader = new FileReader();
27964             
27965             reader.onload = function (e) {
27966                 if (e.target.error) {
27967                     Roo.log(e.target.error);
27968                     return;
27969                 }
27970                 
27971                 var buffer = e.target.result,
27972                     dataView = new DataView(buffer),
27973                     offset = 2,
27974                     maxOffset = dataView.byteLength - 4,
27975                     markerBytes,
27976                     markerLength;
27977                 
27978                 if (dataView.getUint16(0) === 0xffd8) {
27979                     while (offset < maxOffset) {
27980                         markerBytes = dataView.getUint16(offset);
27981                         
27982                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27983                             markerLength = dataView.getUint16(offset + 2) + 2;
27984                             if (offset + markerLength > dataView.byteLength) {
27985                                 Roo.log('Invalid meta data: Invalid segment size.');
27986                                 break;
27987                             }
27988                             
27989                             if(markerBytes == 0xffe1){
27990                                 _this.parseExifData(
27991                                     dataView,
27992                                     offset,
27993                                     markerLength
27994                                 );
27995                             }
27996                             
27997                             offset += markerLength;
27998                             
27999                             continue;
28000                         }
28001                         
28002                         break;
28003                     }
28004                     
28005                 }
28006                 
28007                 var url = _this.urlAPI.createObjectURL(_this.file);
28008                 
28009                 _this.loadCanvas(url);
28010                 
28011                 return;
28012             }
28013             
28014             reader.readAsArrayBuffer(this.file);
28015             
28016         }
28017         
28018     },
28019     
28020     parseExifData : function(dataView, offset, length)
28021     {
28022         var tiffOffset = offset + 10,
28023             littleEndian,
28024             dirOffset;
28025     
28026         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28027             // No Exif data, might be XMP data instead
28028             return;
28029         }
28030         
28031         // Check for the ASCII code for "Exif" (0x45786966):
28032         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28033             // No Exif data, might be XMP data instead
28034             return;
28035         }
28036         if (tiffOffset + 8 > dataView.byteLength) {
28037             Roo.log('Invalid Exif data: Invalid segment size.');
28038             return;
28039         }
28040         // Check for the two null bytes:
28041         if (dataView.getUint16(offset + 8) !== 0x0000) {
28042             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28043             return;
28044         }
28045         // Check the byte alignment:
28046         switch (dataView.getUint16(tiffOffset)) {
28047         case 0x4949:
28048             littleEndian = true;
28049             break;
28050         case 0x4D4D:
28051             littleEndian = false;
28052             break;
28053         default:
28054             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28055             return;
28056         }
28057         // Check for the TIFF tag marker (0x002A):
28058         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28059             Roo.log('Invalid Exif data: Missing TIFF marker.');
28060             return;
28061         }
28062         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28063         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28064         
28065         this.parseExifTags(
28066             dataView,
28067             tiffOffset,
28068             tiffOffset + dirOffset,
28069             littleEndian
28070         );
28071     },
28072     
28073     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28074     {
28075         var tagsNumber,
28076             dirEndOffset,
28077             i;
28078         if (dirOffset + 6 > dataView.byteLength) {
28079             Roo.log('Invalid Exif data: Invalid directory offset.');
28080             return;
28081         }
28082         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28083         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28084         if (dirEndOffset + 4 > dataView.byteLength) {
28085             Roo.log('Invalid Exif data: Invalid directory size.');
28086             return;
28087         }
28088         for (i = 0; i < tagsNumber; i += 1) {
28089             this.parseExifTag(
28090                 dataView,
28091                 tiffOffset,
28092                 dirOffset + 2 + 12 * i, // tag offset
28093                 littleEndian
28094             );
28095         }
28096         // Return the offset to the next directory:
28097         return dataView.getUint32(dirEndOffset, littleEndian);
28098     },
28099     
28100     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28101     {
28102         var tag = dataView.getUint16(offset, littleEndian);
28103         
28104         this.exif[tag] = this.getExifValue(
28105             dataView,
28106             tiffOffset,
28107             offset,
28108             dataView.getUint16(offset + 2, littleEndian), // tag type
28109             dataView.getUint32(offset + 4, littleEndian), // tag length
28110             littleEndian
28111         );
28112     },
28113     
28114     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28115     {
28116         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28117             tagSize,
28118             dataOffset,
28119             values,
28120             i,
28121             str,
28122             c;
28123     
28124         if (!tagType) {
28125             Roo.log('Invalid Exif data: Invalid tag type.');
28126             return;
28127         }
28128         
28129         tagSize = tagType.size * length;
28130         // Determine if the value is contained in the dataOffset bytes,
28131         // or if the value at the dataOffset is a pointer to the actual data:
28132         dataOffset = tagSize > 4 ?
28133                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28134         if (dataOffset + tagSize > dataView.byteLength) {
28135             Roo.log('Invalid Exif data: Invalid data offset.');
28136             return;
28137         }
28138         if (length === 1) {
28139             return tagType.getValue(dataView, dataOffset, littleEndian);
28140         }
28141         values = [];
28142         for (i = 0; i < length; i += 1) {
28143             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28144         }
28145         
28146         if (tagType.ascii) {
28147             str = '';
28148             // Concatenate the chars:
28149             for (i = 0; i < values.length; i += 1) {
28150                 c = values[i];
28151                 // Ignore the terminating NULL byte(s):
28152                 if (c === '\u0000') {
28153                     break;
28154                 }
28155                 str += c;
28156             }
28157             return str;
28158         }
28159         return values;
28160     }
28161     
28162 });
28163
28164 Roo.apply(Roo.bootstrap.UploadCropbox, {
28165     tags : {
28166         'Orientation': 0x0112
28167     },
28168     
28169     Orientation: {
28170             1: 0, //'top-left',
28171 //            2: 'top-right',
28172             3: 180, //'bottom-right',
28173 //            4: 'bottom-left',
28174 //            5: 'left-top',
28175             6: 90, //'right-top',
28176 //            7: 'right-bottom',
28177             8: 270 //'left-bottom'
28178     },
28179     
28180     exifTagTypes : {
28181         // byte, 8-bit unsigned int:
28182         1: {
28183             getValue: function (dataView, dataOffset) {
28184                 return dataView.getUint8(dataOffset);
28185             },
28186             size: 1
28187         },
28188         // ascii, 8-bit byte:
28189         2: {
28190             getValue: function (dataView, dataOffset) {
28191                 return String.fromCharCode(dataView.getUint8(dataOffset));
28192             },
28193             size: 1,
28194             ascii: true
28195         },
28196         // short, 16 bit int:
28197         3: {
28198             getValue: function (dataView, dataOffset, littleEndian) {
28199                 return dataView.getUint16(dataOffset, littleEndian);
28200             },
28201             size: 2
28202         },
28203         // long, 32 bit int:
28204         4: {
28205             getValue: function (dataView, dataOffset, littleEndian) {
28206                 return dataView.getUint32(dataOffset, littleEndian);
28207             },
28208             size: 4
28209         },
28210         // rational = two long values, first is numerator, second is denominator:
28211         5: {
28212             getValue: function (dataView, dataOffset, littleEndian) {
28213                 return dataView.getUint32(dataOffset, littleEndian) /
28214                     dataView.getUint32(dataOffset + 4, littleEndian);
28215             },
28216             size: 8
28217         },
28218         // slong, 32 bit signed int:
28219         9: {
28220             getValue: function (dataView, dataOffset, littleEndian) {
28221                 return dataView.getInt32(dataOffset, littleEndian);
28222             },
28223             size: 4
28224         },
28225         // srational, two slongs, first is numerator, second is denominator:
28226         10: {
28227             getValue: function (dataView, dataOffset, littleEndian) {
28228                 return dataView.getInt32(dataOffset, littleEndian) /
28229                     dataView.getInt32(dataOffset + 4, littleEndian);
28230             },
28231             size: 8
28232         }
28233     },
28234     
28235     footer : {
28236         STANDARD : [
28237             {
28238                 tag : 'div',
28239                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28240                 action : 'rotate-left',
28241                 cn : [
28242                     {
28243                         tag : 'button',
28244                         cls : 'btn btn-default',
28245                         html : '<i class="fa fa-undo"></i>'
28246                     }
28247                 ]
28248             },
28249             {
28250                 tag : 'div',
28251                 cls : 'btn-group roo-upload-cropbox-picture',
28252                 action : 'picture',
28253                 cn : [
28254                     {
28255                         tag : 'button',
28256                         cls : 'btn btn-default',
28257                         html : '<i class="fa fa-picture-o"></i>'
28258                     }
28259                 ]
28260             },
28261             {
28262                 tag : 'div',
28263                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28264                 action : 'rotate-right',
28265                 cn : [
28266                     {
28267                         tag : 'button',
28268                         cls : 'btn btn-default',
28269                         html : '<i class="fa fa-repeat"></i>'
28270                     }
28271                 ]
28272             }
28273         ],
28274         DOCUMENT : [
28275             {
28276                 tag : 'div',
28277                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28278                 action : 'rotate-left',
28279                 cn : [
28280                     {
28281                         tag : 'button',
28282                         cls : 'btn btn-default',
28283                         html : '<i class="fa fa-undo"></i>'
28284                     }
28285                 ]
28286             },
28287             {
28288                 tag : 'div',
28289                 cls : 'btn-group roo-upload-cropbox-download',
28290                 action : 'download',
28291                 cn : [
28292                     {
28293                         tag : 'button',
28294                         cls : 'btn btn-default',
28295                         html : '<i class="fa fa-download"></i>'
28296                     }
28297                 ]
28298             },
28299             {
28300                 tag : 'div',
28301                 cls : 'btn-group roo-upload-cropbox-crop',
28302                 action : 'crop',
28303                 cn : [
28304                     {
28305                         tag : 'button',
28306                         cls : 'btn btn-default',
28307                         html : '<i class="fa fa-crop"></i>'
28308                     }
28309                 ]
28310             },
28311             {
28312                 tag : 'div',
28313                 cls : 'btn-group roo-upload-cropbox-trash',
28314                 action : 'trash',
28315                 cn : [
28316                     {
28317                         tag : 'button',
28318                         cls : 'btn btn-default',
28319                         html : '<i class="fa fa-trash"></i>'
28320                     }
28321                 ]
28322             },
28323             {
28324                 tag : 'div',
28325                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28326                 action : 'rotate-right',
28327                 cn : [
28328                     {
28329                         tag : 'button',
28330                         cls : 'btn btn-default',
28331                         html : '<i class="fa fa-repeat"></i>'
28332                     }
28333                 ]
28334             }
28335         ],
28336         ROTATOR : [
28337             {
28338                 tag : 'div',
28339                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28340                 action : 'rotate-left',
28341                 cn : [
28342                     {
28343                         tag : 'button',
28344                         cls : 'btn btn-default',
28345                         html : '<i class="fa fa-undo"></i>'
28346                     }
28347                 ]
28348             },
28349             {
28350                 tag : 'div',
28351                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28352                 action : 'rotate-right',
28353                 cn : [
28354                     {
28355                         tag : 'button',
28356                         cls : 'btn btn-default',
28357                         html : '<i class="fa fa-repeat"></i>'
28358                     }
28359                 ]
28360             }
28361         ]
28362     }
28363 });
28364
28365 /*
28366 * Licence: LGPL
28367 */
28368
28369 /**
28370  * @class Roo.bootstrap.DocumentManager
28371  * @extends Roo.bootstrap.Component
28372  * Bootstrap DocumentManager class
28373  * @cfg {String} paramName default 'imageUpload'
28374  * @cfg {String} toolTipName default 'filename'
28375  * @cfg {String} method default POST
28376  * @cfg {String} url action url
28377  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28378  * @cfg {Boolean} multiple multiple upload default true
28379  * @cfg {Number} thumbSize default 300
28380  * @cfg {String} fieldLabel
28381  * @cfg {Number} labelWidth default 4
28382  * @cfg {String} labelAlign (left|top) default left
28383  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28384 * @cfg {Number} labellg set the width of label (1-12)
28385  * @cfg {Number} labelmd set the width of label (1-12)
28386  * @cfg {Number} labelsm set the width of label (1-12)
28387  * @cfg {Number} labelxs set the width of label (1-12)
28388  * 
28389  * @constructor
28390  * Create a new DocumentManager
28391  * @param {Object} config The config object
28392  */
28393
28394 Roo.bootstrap.DocumentManager = function(config){
28395     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28396     
28397     this.files = [];
28398     this.delegates = [];
28399     
28400     this.addEvents({
28401         /**
28402          * @event initial
28403          * Fire when initial the DocumentManager
28404          * @param {Roo.bootstrap.DocumentManager} this
28405          */
28406         "initial" : true,
28407         /**
28408          * @event inspect
28409          * inspect selected file
28410          * @param {Roo.bootstrap.DocumentManager} this
28411          * @param {File} file
28412          */
28413         "inspect" : true,
28414         /**
28415          * @event exception
28416          * Fire when xhr load exception
28417          * @param {Roo.bootstrap.DocumentManager} this
28418          * @param {XMLHttpRequest} xhr
28419          */
28420         "exception" : true,
28421         /**
28422          * @event afterupload
28423          * Fire when xhr load exception
28424          * @param {Roo.bootstrap.DocumentManager} this
28425          * @param {XMLHttpRequest} xhr
28426          */
28427         "afterupload" : true,
28428         /**
28429          * @event prepare
28430          * prepare the form data
28431          * @param {Roo.bootstrap.DocumentManager} this
28432          * @param {Object} formData
28433          */
28434         "prepare" : true,
28435         /**
28436          * @event remove
28437          * Fire when remove the file
28438          * @param {Roo.bootstrap.DocumentManager} this
28439          * @param {Object} file
28440          */
28441         "remove" : true,
28442         /**
28443          * @event refresh
28444          * Fire after refresh the file
28445          * @param {Roo.bootstrap.DocumentManager} this
28446          */
28447         "refresh" : true,
28448         /**
28449          * @event click
28450          * Fire after click the image
28451          * @param {Roo.bootstrap.DocumentManager} this
28452          * @param {Object} file
28453          */
28454         "click" : true,
28455         /**
28456          * @event edit
28457          * Fire when upload a image and editable set to true
28458          * @param {Roo.bootstrap.DocumentManager} this
28459          * @param {Object} file
28460          */
28461         "edit" : true,
28462         /**
28463          * @event beforeselectfile
28464          * Fire before select file
28465          * @param {Roo.bootstrap.DocumentManager} this
28466          */
28467         "beforeselectfile" : true,
28468         /**
28469          * @event process
28470          * Fire before process file
28471          * @param {Roo.bootstrap.DocumentManager} this
28472          * @param {Object} file
28473          */
28474         "process" : true,
28475         /**
28476          * @event previewrendered
28477          * Fire when preview rendered
28478          * @param {Roo.bootstrap.DocumentManager} this
28479          * @param {Object} file
28480          */
28481         "previewrendered" : true
28482         
28483     });
28484 };
28485
28486 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28487     
28488     boxes : 0,
28489     inputName : '',
28490     thumbSize : 300,
28491     multiple : true,
28492     files : false,
28493     method : 'POST',
28494     url : '',
28495     paramName : 'imageUpload',
28496     toolTipName : 'filename',
28497     fieldLabel : '',
28498     labelWidth : 4,
28499     labelAlign : 'left',
28500     editable : true,
28501     delegates : false,
28502     xhr : false, 
28503     
28504     labellg : 0,
28505     labelmd : 0,
28506     labelsm : 0,
28507     labelxs : 0,
28508     
28509     getAutoCreate : function()
28510     {   
28511         var managerWidget = {
28512             tag : 'div',
28513             cls : 'roo-document-manager',
28514             cn : [
28515                 {
28516                     tag : 'input',
28517                     cls : 'roo-document-manager-selector',
28518                     type : 'file'
28519                 },
28520                 {
28521                     tag : 'div',
28522                     cls : 'roo-document-manager-uploader',
28523                     cn : [
28524                         {
28525                             tag : 'div',
28526                             cls : 'roo-document-manager-upload-btn',
28527                             html : '<i class="fa fa-plus"></i>'
28528                         }
28529                     ]
28530                     
28531                 }
28532             ]
28533         };
28534         
28535         var content = [
28536             {
28537                 tag : 'div',
28538                 cls : 'column col-md-12',
28539                 cn : managerWidget
28540             }
28541         ];
28542         
28543         if(this.fieldLabel.length){
28544             
28545             content = [
28546                 {
28547                     tag : 'div',
28548                     cls : 'column col-md-12',
28549                     html : this.fieldLabel
28550                 },
28551                 {
28552                     tag : 'div',
28553                     cls : 'column col-md-12',
28554                     cn : managerWidget
28555                 }
28556             ];
28557
28558             if(this.labelAlign == 'left'){
28559                 content = [
28560                     {
28561                         tag : 'div',
28562                         cls : 'column',
28563                         html : this.fieldLabel
28564                     },
28565                     {
28566                         tag : 'div',
28567                         cls : 'column',
28568                         cn : managerWidget
28569                     }
28570                 ];
28571                 
28572                 if(this.labelWidth > 12){
28573                     content[0].style = "width: " + this.labelWidth + 'px';
28574                 }
28575
28576                 if(this.labelWidth < 13 && this.labelmd == 0){
28577                     this.labelmd = this.labelWidth;
28578                 }
28579
28580                 if(this.labellg > 0){
28581                     content[0].cls += ' col-lg-' + this.labellg;
28582                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28583                 }
28584
28585                 if(this.labelmd > 0){
28586                     content[0].cls += ' col-md-' + this.labelmd;
28587                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28588                 }
28589
28590                 if(this.labelsm > 0){
28591                     content[0].cls += ' col-sm-' + this.labelsm;
28592                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28593                 }
28594
28595                 if(this.labelxs > 0){
28596                     content[0].cls += ' col-xs-' + this.labelxs;
28597                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28598                 }
28599                 
28600             }
28601         }
28602         
28603         var cfg = {
28604             tag : 'div',
28605             cls : 'row clearfix',
28606             cn : content
28607         };
28608         
28609         return cfg;
28610         
28611     },
28612     
28613     initEvents : function()
28614     {
28615         this.managerEl = this.el.select('.roo-document-manager', true).first();
28616         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28617         
28618         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28619         this.selectorEl.hide();
28620         
28621         if(this.multiple){
28622             this.selectorEl.attr('multiple', 'multiple');
28623         }
28624         
28625         this.selectorEl.on('change', this.onFileSelected, this);
28626         
28627         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28628         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28629         
28630         this.uploader.on('click', this.onUploaderClick, this);
28631         
28632         this.renderProgressDialog();
28633         
28634         var _this = this;
28635         
28636         window.addEventListener("resize", function() { _this.refresh(); } );
28637         
28638         this.fireEvent('initial', this);
28639     },
28640     
28641     renderProgressDialog : function()
28642     {
28643         var _this = this;
28644         
28645         this.progressDialog = new Roo.bootstrap.Modal({
28646             cls : 'roo-document-manager-progress-dialog',
28647             allow_close : false,
28648             title : '',
28649             buttons : [
28650                 {
28651                     name  :'cancel',
28652                     weight : 'danger',
28653                     html : 'Cancel'
28654                 }
28655             ], 
28656             listeners : { 
28657                 btnclick : function() {
28658                     _this.uploadCancel();
28659                     this.hide();
28660                 }
28661             }
28662         });
28663          
28664         this.progressDialog.render(Roo.get(document.body));
28665          
28666         this.progress = new Roo.bootstrap.Progress({
28667             cls : 'roo-document-manager-progress',
28668             active : true,
28669             striped : true
28670         });
28671         
28672         this.progress.render(this.progressDialog.getChildContainer());
28673         
28674         this.progressBar = new Roo.bootstrap.ProgressBar({
28675             cls : 'roo-document-manager-progress-bar',
28676             aria_valuenow : 0,
28677             aria_valuemin : 0,
28678             aria_valuemax : 12,
28679             panel : 'success'
28680         });
28681         
28682         this.progressBar.render(this.progress.getChildContainer());
28683     },
28684     
28685     onUploaderClick : function(e)
28686     {
28687         e.preventDefault();
28688      
28689         if(this.fireEvent('beforeselectfile', this) != false){
28690             this.selectorEl.dom.click();
28691         }
28692         
28693     },
28694     
28695     onFileSelected : function(e)
28696     {
28697         e.preventDefault();
28698         
28699         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28700             return;
28701         }
28702         
28703         Roo.each(this.selectorEl.dom.files, function(file){
28704             if(this.fireEvent('inspect', this, file) != false){
28705                 this.files.push(file);
28706             }
28707         }, this);
28708         
28709         this.queue();
28710         
28711     },
28712     
28713     queue : function()
28714     {
28715         this.selectorEl.dom.value = '';
28716         
28717         if(!this.files || !this.files.length){
28718             return;
28719         }
28720         
28721         if(this.boxes > 0 && this.files.length > this.boxes){
28722             this.files = this.files.slice(0, this.boxes);
28723         }
28724         
28725         this.uploader.show();
28726         
28727         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28728             this.uploader.hide();
28729         }
28730         
28731         var _this = this;
28732         
28733         var files = [];
28734         
28735         var docs = [];
28736         
28737         Roo.each(this.files, function(file){
28738             
28739             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28740                 var f = this.renderPreview(file);
28741                 files.push(f);
28742                 return;
28743             }
28744             
28745             if(file.type.indexOf('image') != -1){
28746                 this.delegates.push(
28747                     (function(){
28748                         _this.process(file);
28749                     }).createDelegate(this)
28750                 );
28751         
28752                 return;
28753             }
28754             
28755             docs.push(
28756                 (function(){
28757                     _this.process(file);
28758                 }).createDelegate(this)
28759             );
28760             
28761         }, this);
28762         
28763         this.files = files;
28764         
28765         this.delegates = this.delegates.concat(docs);
28766         
28767         if(!this.delegates.length){
28768             this.refresh();
28769             return;
28770         }
28771         
28772         this.progressBar.aria_valuemax = this.delegates.length;
28773         
28774         this.arrange();
28775         
28776         return;
28777     },
28778     
28779     arrange : function()
28780     {
28781         if(!this.delegates.length){
28782             this.progressDialog.hide();
28783             this.refresh();
28784             return;
28785         }
28786         
28787         var delegate = this.delegates.shift();
28788         
28789         this.progressDialog.show();
28790         
28791         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28792         
28793         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28794         
28795         delegate();
28796     },
28797     
28798     refresh : function()
28799     {
28800         this.uploader.show();
28801         
28802         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28803             this.uploader.hide();
28804         }
28805         
28806         Roo.isTouch ? this.closable(false) : this.closable(true);
28807         
28808         this.fireEvent('refresh', this);
28809     },
28810     
28811     onRemove : function(e, el, o)
28812     {
28813         e.preventDefault();
28814         
28815         this.fireEvent('remove', this, o);
28816         
28817     },
28818     
28819     remove : function(o)
28820     {
28821         var files = [];
28822         
28823         Roo.each(this.files, function(file){
28824             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28825                 files.push(file);
28826                 return;
28827             }
28828
28829             o.target.remove();
28830
28831         }, this);
28832         
28833         this.files = files;
28834         
28835         this.refresh();
28836     },
28837     
28838     clear : function()
28839     {
28840         Roo.each(this.files, function(file){
28841             if(!file.target){
28842                 return;
28843             }
28844             
28845             file.target.remove();
28846
28847         }, this);
28848         
28849         this.files = [];
28850         
28851         this.refresh();
28852     },
28853     
28854     onClick : function(e, el, o)
28855     {
28856         e.preventDefault();
28857         
28858         this.fireEvent('click', this, o);
28859         
28860     },
28861     
28862     closable : function(closable)
28863     {
28864         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28865             
28866             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28867             
28868             if(closable){
28869                 el.show();
28870                 return;
28871             }
28872             
28873             el.hide();
28874             
28875         }, this);
28876     },
28877     
28878     xhrOnLoad : function(xhr)
28879     {
28880         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28881             el.remove();
28882         }, this);
28883         
28884         if (xhr.readyState !== 4) {
28885             this.arrange();
28886             this.fireEvent('exception', this, xhr);
28887             return;
28888         }
28889
28890         var response = Roo.decode(xhr.responseText);
28891         
28892         if(!response.success){
28893             this.arrange();
28894             this.fireEvent('exception', this, xhr);
28895             return;
28896         }
28897         
28898         var file = this.renderPreview(response.data);
28899         
28900         this.files.push(file);
28901         
28902         this.arrange();
28903         
28904         this.fireEvent('afterupload', this, xhr);
28905         
28906     },
28907     
28908     xhrOnError : function(xhr)
28909     {
28910         Roo.log('xhr on error');
28911         
28912         var response = Roo.decode(xhr.responseText);
28913           
28914         Roo.log(response);
28915         
28916         this.arrange();
28917     },
28918     
28919     process : function(file)
28920     {
28921         if(this.fireEvent('process', this, file) !== false){
28922             if(this.editable && file.type.indexOf('image') != -1){
28923                 this.fireEvent('edit', this, file);
28924                 return;
28925             }
28926
28927             this.uploadStart(file, false);
28928
28929             return;
28930         }
28931         
28932     },
28933     
28934     uploadStart : function(file, crop)
28935     {
28936         this.xhr = new XMLHttpRequest();
28937         
28938         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28939             this.arrange();
28940             return;
28941         }
28942         
28943         file.xhr = this.xhr;
28944             
28945         this.managerEl.createChild({
28946             tag : 'div',
28947             cls : 'roo-document-manager-loading',
28948             cn : [
28949                 {
28950                     tag : 'div',
28951                     tooltip : file.name,
28952                     cls : 'roo-document-manager-thumb',
28953                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28954                 }
28955             ]
28956
28957         });
28958
28959         this.xhr.open(this.method, this.url, true);
28960         
28961         var headers = {
28962             "Accept": "application/json",
28963             "Cache-Control": "no-cache",
28964             "X-Requested-With": "XMLHttpRequest"
28965         };
28966         
28967         for (var headerName in headers) {
28968             var headerValue = headers[headerName];
28969             if (headerValue) {
28970                 this.xhr.setRequestHeader(headerName, headerValue);
28971             }
28972         }
28973         
28974         var _this = this;
28975         
28976         this.xhr.onload = function()
28977         {
28978             _this.xhrOnLoad(_this.xhr);
28979         }
28980         
28981         this.xhr.onerror = function()
28982         {
28983             _this.xhrOnError(_this.xhr);
28984         }
28985         
28986         var formData = new FormData();
28987
28988         formData.append('returnHTML', 'NO');
28989         
28990         if(crop){
28991             formData.append('crop', crop);
28992         }
28993         
28994         formData.append(this.paramName, file, file.name);
28995         
28996         var options = {
28997             file : file, 
28998             manually : false
28999         };
29000         
29001         if(this.fireEvent('prepare', this, formData, options) != false){
29002             
29003             if(options.manually){
29004                 return;
29005             }
29006             
29007             this.xhr.send(formData);
29008             return;
29009         };
29010         
29011         this.uploadCancel();
29012     },
29013     
29014     uploadCancel : function()
29015     {
29016         if (this.xhr) {
29017             this.xhr.abort();
29018         }
29019         
29020         this.delegates = [];
29021         
29022         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29023             el.remove();
29024         }, this);
29025         
29026         this.arrange();
29027     },
29028     
29029     renderPreview : function(file)
29030     {
29031         if(typeof(file.target) != 'undefined' && file.target){
29032             return file;
29033         }
29034         
29035         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29036         
29037         var previewEl = this.managerEl.createChild({
29038             tag : 'div',
29039             cls : 'roo-document-manager-preview',
29040             cn : [
29041                 {
29042                     tag : 'div',
29043                     tooltip : file[this.toolTipName],
29044                     cls : 'roo-document-manager-thumb',
29045                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29046                 },
29047                 {
29048                     tag : 'button',
29049                     cls : 'close',
29050                     html : '<i class="fa fa-times-circle"></i>'
29051                 }
29052             ]
29053         });
29054
29055         var close = previewEl.select('button.close', true).first();
29056
29057         close.on('click', this.onRemove, this, file);
29058
29059         file.target = previewEl;
29060
29061         var image = previewEl.select('img', true).first();
29062         
29063         var _this = this;
29064         
29065         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29066         
29067         image.on('click', this.onClick, this, file);
29068         
29069         this.fireEvent('previewrendered', this, file);
29070         
29071         return file;
29072         
29073     },
29074     
29075     onPreviewLoad : function(file, image)
29076     {
29077         if(typeof(file.target) == 'undefined' || !file.target){
29078             return;
29079         }
29080         
29081         var width = image.dom.naturalWidth || image.dom.width;
29082         var height = image.dom.naturalHeight || image.dom.height;
29083         
29084         if(width > height){
29085             file.target.addClass('wide');
29086             return;
29087         }
29088         
29089         file.target.addClass('tall');
29090         return;
29091         
29092     },
29093     
29094     uploadFromSource : function(file, crop)
29095     {
29096         this.xhr = new XMLHttpRequest();
29097         
29098         this.managerEl.createChild({
29099             tag : 'div',
29100             cls : 'roo-document-manager-loading',
29101             cn : [
29102                 {
29103                     tag : 'div',
29104                     tooltip : file.name,
29105                     cls : 'roo-document-manager-thumb',
29106                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29107                 }
29108             ]
29109
29110         });
29111
29112         this.xhr.open(this.method, this.url, true);
29113         
29114         var headers = {
29115             "Accept": "application/json",
29116             "Cache-Control": "no-cache",
29117             "X-Requested-With": "XMLHttpRequest"
29118         };
29119         
29120         for (var headerName in headers) {
29121             var headerValue = headers[headerName];
29122             if (headerValue) {
29123                 this.xhr.setRequestHeader(headerName, headerValue);
29124             }
29125         }
29126         
29127         var _this = this;
29128         
29129         this.xhr.onload = function()
29130         {
29131             _this.xhrOnLoad(_this.xhr);
29132         }
29133         
29134         this.xhr.onerror = function()
29135         {
29136             _this.xhrOnError(_this.xhr);
29137         }
29138         
29139         var formData = new FormData();
29140
29141         formData.append('returnHTML', 'NO');
29142         
29143         formData.append('crop', crop);
29144         
29145         if(typeof(file.filename) != 'undefined'){
29146             formData.append('filename', file.filename);
29147         }
29148         
29149         if(typeof(file.mimetype) != 'undefined'){
29150             formData.append('mimetype', file.mimetype);
29151         }
29152         
29153         Roo.log(formData);
29154         
29155         if(this.fireEvent('prepare', this, formData) != false){
29156             this.xhr.send(formData);
29157         };
29158     }
29159 });
29160
29161 /*
29162 * Licence: LGPL
29163 */
29164
29165 /**
29166  * @class Roo.bootstrap.DocumentViewer
29167  * @extends Roo.bootstrap.Component
29168  * Bootstrap DocumentViewer class
29169  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29170  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29171  * 
29172  * @constructor
29173  * Create a new DocumentViewer
29174  * @param {Object} config The config object
29175  */
29176
29177 Roo.bootstrap.DocumentViewer = function(config){
29178     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29179     
29180     this.addEvents({
29181         /**
29182          * @event initial
29183          * Fire after initEvent
29184          * @param {Roo.bootstrap.DocumentViewer} this
29185          */
29186         "initial" : true,
29187         /**
29188          * @event click
29189          * Fire after click
29190          * @param {Roo.bootstrap.DocumentViewer} this
29191          */
29192         "click" : true,
29193         /**
29194          * @event download
29195          * Fire after download button
29196          * @param {Roo.bootstrap.DocumentViewer} this
29197          */
29198         "download" : true,
29199         /**
29200          * @event trash
29201          * Fire after trash button
29202          * @param {Roo.bootstrap.DocumentViewer} this
29203          */
29204         "trash" : true
29205         
29206     });
29207 };
29208
29209 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29210     
29211     showDownload : true,
29212     
29213     showTrash : true,
29214     
29215     getAutoCreate : function()
29216     {
29217         var cfg = {
29218             tag : 'div',
29219             cls : 'roo-document-viewer',
29220             cn : [
29221                 {
29222                     tag : 'div',
29223                     cls : 'roo-document-viewer-body',
29224                     cn : [
29225                         {
29226                             tag : 'div',
29227                             cls : 'roo-document-viewer-thumb',
29228                             cn : [
29229                                 {
29230                                     tag : 'img',
29231                                     cls : 'roo-document-viewer-image'
29232                                 }
29233                             ]
29234                         }
29235                     ]
29236                 },
29237                 {
29238                     tag : 'div',
29239                     cls : 'roo-document-viewer-footer',
29240                     cn : {
29241                         tag : 'div',
29242                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29243                         cn : [
29244                             {
29245                                 tag : 'div',
29246                                 cls : 'btn-group roo-document-viewer-download',
29247                                 cn : [
29248                                     {
29249                                         tag : 'button',
29250                                         cls : 'btn btn-default',
29251                                         html : '<i class="fa fa-download"></i>'
29252                                     }
29253                                 ]
29254                             },
29255                             {
29256                                 tag : 'div',
29257                                 cls : 'btn-group roo-document-viewer-trash',
29258                                 cn : [
29259                                     {
29260                                         tag : 'button',
29261                                         cls : 'btn btn-default',
29262                                         html : '<i class="fa fa-trash"></i>'
29263                                     }
29264                                 ]
29265                             }
29266                         ]
29267                     }
29268                 }
29269             ]
29270         };
29271         
29272         return cfg;
29273     },
29274     
29275     initEvents : function()
29276     {
29277         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29278         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29279         
29280         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29281         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29282         
29283         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29284         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29285         
29286         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29287         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29288         
29289         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29290         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29291         
29292         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29293         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29294         
29295         this.bodyEl.on('click', this.onClick, this);
29296         this.downloadBtn.on('click', this.onDownload, this);
29297         this.trashBtn.on('click', this.onTrash, this);
29298         
29299         this.downloadBtn.hide();
29300         this.trashBtn.hide();
29301         
29302         if(this.showDownload){
29303             this.downloadBtn.show();
29304         }
29305         
29306         if(this.showTrash){
29307             this.trashBtn.show();
29308         }
29309         
29310         if(!this.showDownload && !this.showTrash) {
29311             this.footerEl.hide();
29312         }
29313         
29314     },
29315     
29316     initial : function()
29317     {
29318         this.fireEvent('initial', this);
29319         
29320     },
29321     
29322     onClick : function(e)
29323     {
29324         e.preventDefault();
29325         
29326         this.fireEvent('click', this);
29327     },
29328     
29329     onDownload : function(e)
29330     {
29331         e.preventDefault();
29332         
29333         this.fireEvent('download', this);
29334     },
29335     
29336     onTrash : function(e)
29337     {
29338         e.preventDefault();
29339         
29340         this.fireEvent('trash', this);
29341     }
29342     
29343 });
29344 /*
29345  * - LGPL
29346  *
29347  * nav progress bar
29348  * 
29349  */
29350
29351 /**
29352  * @class Roo.bootstrap.NavProgressBar
29353  * @extends Roo.bootstrap.Component
29354  * Bootstrap NavProgressBar class
29355  * 
29356  * @constructor
29357  * Create a new nav progress bar
29358  * @param {Object} config The config object
29359  */
29360
29361 Roo.bootstrap.NavProgressBar = function(config){
29362     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29363
29364     this.bullets = this.bullets || [];
29365    
29366 //    Roo.bootstrap.NavProgressBar.register(this);
29367      this.addEvents({
29368         /**
29369              * @event changed
29370              * Fires when the active item changes
29371              * @param {Roo.bootstrap.NavProgressBar} this
29372              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29373              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29374          */
29375         'changed': true
29376      });
29377     
29378 };
29379
29380 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29381     
29382     bullets : [],
29383     barItems : [],
29384     
29385     getAutoCreate : function()
29386     {
29387         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29388         
29389         cfg = {
29390             tag : 'div',
29391             cls : 'roo-navigation-bar-group',
29392             cn : [
29393                 {
29394                     tag : 'div',
29395                     cls : 'roo-navigation-top-bar'
29396                 },
29397                 {
29398                     tag : 'div',
29399                     cls : 'roo-navigation-bullets-bar',
29400                     cn : [
29401                         {
29402                             tag : 'ul',
29403                             cls : 'roo-navigation-bar'
29404                         }
29405                     ]
29406                 },
29407                 
29408                 {
29409                     tag : 'div',
29410                     cls : 'roo-navigation-bottom-bar'
29411                 }
29412             ]
29413             
29414         };
29415         
29416         return cfg;
29417         
29418     },
29419     
29420     initEvents: function() 
29421     {
29422         
29423     },
29424     
29425     onRender : function(ct, position) 
29426     {
29427         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29428         
29429         if(this.bullets.length){
29430             Roo.each(this.bullets, function(b){
29431                this.addItem(b);
29432             }, this);
29433         }
29434         
29435         this.format();
29436         
29437     },
29438     
29439     addItem : function(cfg)
29440     {
29441         var item = new Roo.bootstrap.NavProgressItem(cfg);
29442         
29443         item.parentId = this.id;
29444         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29445         
29446         if(cfg.html){
29447             var top = new Roo.bootstrap.Element({
29448                 tag : 'div',
29449                 cls : 'roo-navigation-bar-text'
29450             });
29451             
29452             var bottom = new Roo.bootstrap.Element({
29453                 tag : 'div',
29454                 cls : 'roo-navigation-bar-text'
29455             });
29456             
29457             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29458             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29459             
29460             var topText = new Roo.bootstrap.Element({
29461                 tag : 'span',
29462                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29463             });
29464             
29465             var bottomText = new Roo.bootstrap.Element({
29466                 tag : 'span',
29467                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29468             });
29469             
29470             topText.onRender(top.el, null);
29471             bottomText.onRender(bottom.el, null);
29472             
29473             item.topEl = top;
29474             item.bottomEl = bottom;
29475         }
29476         
29477         this.barItems.push(item);
29478         
29479         return item;
29480     },
29481     
29482     getActive : function()
29483     {
29484         var active = false;
29485         
29486         Roo.each(this.barItems, function(v){
29487             
29488             if (!v.isActive()) {
29489                 return;
29490             }
29491             
29492             active = v;
29493             return false;
29494             
29495         });
29496         
29497         return active;
29498     },
29499     
29500     setActiveItem : function(item)
29501     {
29502         var prev = false;
29503         
29504         Roo.each(this.barItems, function(v){
29505             if (v.rid == item.rid) {
29506                 return ;
29507             }
29508             
29509             if (v.isActive()) {
29510                 v.setActive(false);
29511                 prev = v;
29512             }
29513         });
29514
29515         item.setActive(true);
29516         
29517         this.fireEvent('changed', this, item, prev);
29518     },
29519     
29520     getBarItem: function(rid)
29521     {
29522         var ret = false;
29523         
29524         Roo.each(this.barItems, function(e) {
29525             if (e.rid != rid) {
29526                 return;
29527             }
29528             
29529             ret =  e;
29530             return false;
29531         });
29532         
29533         return ret;
29534     },
29535     
29536     indexOfItem : function(item)
29537     {
29538         var index = false;
29539         
29540         Roo.each(this.barItems, function(v, i){
29541             
29542             if (v.rid != item.rid) {
29543                 return;
29544             }
29545             
29546             index = i;
29547             return false
29548         });
29549         
29550         return index;
29551     },
29552     
29553     setActiveNext : function()
29554     {
29555         var i = this.indexOfItem(this.getActive());
29556         
29557         if (i > this.barItems.length) {
29558             return;
29559         }
29560         
29561         this.setActiveItem(this.barItems[i+1]);
29562     },
29563     
29564     setActivePrev : function()
29565     {
29566         var i = this.indexOfItem(this.getActive());
29567         
29568         if (i  < 1) {
29569             return;
29570         }
29571         
29572         this.setActiveItem(this.barItems[i-1]);
29573     },
29574     
29575     format : function()
29576     {
29577         if(!this.barItems.length){
29578             return;
29579         }
29580      
29581         var width = 100 / this.barItems.length;
29582         
29583         Roo.each(this.barItems, function(i){
29584             i.el.setStyle('width', width + '%');
29585             i.topEl.el.setStyle('width', width + '%');
29586             i.bottomEl.el.setStyle('width', width + '%');
29587         }, this);
29588         
29589     }
29590     
29591 });
29592 /*
29593  * - LGPL
29594  *
29595  * Nav Progress Item
29596  * 
29597  */
29598
29599 /**
29600  * @class Roo.bootstrap.NavProgressItem
29601  * @extends Roo.bootstrap.Component
29602  * Bootstrap NavProgressItem class
29603  * @cfg {String} rid the reference id
29604  * @cfg {Boolean} active (true|false) Is item active default false
29605  * @cfg {Boolean} disabled (true|false) Is item active default false
29606  * @cfg {String} html
29607  * @cfg {String} position (top|bottom) text position default bottom
29608  * @cfg {String} icon show icon instead of number
29609  * 
29610  * @constructor
29611  * Create a new NavProgressItem
29612  * @param {Object} config The config object
29613  */
29614 Roo.bootstrap.NavProgressItem = function(config){
29615     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29616     this.addEvents({
29617         // raw events
29618         /**
29619          * @event click
29620          * The raw click event for the entire grid.
29621          * @param {Roo.bootstrap.NavProgressItem} this
29622          * @param {Roo.EventObject} e
29623          */
29624         "click" : true
29625     });
29626    
29627 };
29628
29629 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29630     
29631     rid : '',
29632     active : false,
29633     disabled : false,
29634     html : '',
29635     position : 'bottom',
29636     icon : false,
29637     
29638     getAutoCreate : function()
29639     {
29640         var iconCls = 'roo-navigation-bar-item-icon';
29641         
29642         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29643         
29644         var cfg = {
29645             tag: 'li',
29646             cls: 'roo-navigation-bar-item',
29647             cn : [
29648                 {
29649                     tag : 'i',
29650                     cls : iconCls
29651                 }
29652             ]
29653         };
29654         
29655         if(this.active){
29656             cfg.cls += ' active';
29657         }
29658         if(this.disabled){
29659             cfg.cls += ' disabled';
29660         }
29661         
29662         return cfg;
29663     },
29664     
29665     disable : function()
29666     {
29667         this.setDisabled(true);
29668     },
29669     
29670     enable : function()
29671     {
29672         this.setDisabled(false);
29673     },
29674     
29675     initEvents: function() 
29676     {
29677         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29678         
29679         this.iconEl.on('click', this.onClick, this);
29680     },
29681     
29682     onClick : function(e)
29683     {
29684         e.preventDefault();
29685         
29686         if(this.disabled){
29687             return;
29688         }
29689         
29690         if(this.fireEvent('click', this, e) === false){
29691             return;
29692         };
29693         
29694         this.parent().setActiveItem(this);
29695     },
29696     
29697     isActive: function () 
29698     {
29699         return this.active;
29700     },
29701     
29702     setActive : function(state)
29703     {
29704         if(this.active == state){
29705             return;
29706         }
29707         
29708         this.active = state;
29709         
29710         if (state) {
29711             this.el.addClass('active');
29712             return;
29713         }
29714         
29715         this.el.removeClass('active');
29716         
29717         return;
29718     },
29719     
29720     setDisabled : function(state)
29721     {
29722         if(this.disabled == state){
29723             return;
29724         }
29725         
29726         this.disabled = state;
29727         
29728         if (state) {
29729             this.el.addClass('disabled');
29730             return;
29731         }
29732         
29733         this.el.removeClass('disabled');
29734     },
29735     
29736     tooltipEl : function()
29737     {
29738         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29739     }
29740 });
29741  
29742
29743  /*
29744  * - LGPL
29745  *
29746  * FieldLabel
29747  * 
29748  */
29749
29750 /**
29751  * @class Roo.bootstrap.FieldLabel
29752  * @extends Roo.bootstrap.Component
29753  * Bootstrap FieldLabel class
29754  * @cfg {String} html contents of the element
29755  * @cfg {String} tag tag of the element default label
29756  * @cfg {String} cls class of the element
29757  * @cfg {String} target label target 
29758  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29759  * @cfg {String} invalidClass default "text-warning"
29760  * @cfg {String} validClass default "text-success"
29761  * @cfg {String} iconTooltip default "This field is required"
29762  * @cfg {String} indicatorpos (left|right) default left
29763  * 
29764  * @constructor
29765  * Create a new FieldLabel
29766  * @param {Object} config The config object
29767  */
29768
29769 Roo.bootstrap.FieldLabel = function(config){
29770     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29771     
29772     this.addEvents({
29773             /**
29774              * @event invalid
29775              * Fires after the field has been marked as invalid.
29776              * @param {Roo.form.FieldLabel} this
29777              * @param {String} msg The validation message
29778              */
29779             invalid : true,
29780             /**
29781              * @event valid
29782              * Fires after the field has been validated with no errors.
29783              * @param {Roo.form.FieldLabel} this
29784              */
29785             valid : true
29786         });
29787 };
29788
29789 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29790     
29791     tag: 'label',
29792     cls: '',
29793     html: '',
29794     target: '',
29795     allowBlank : true,
29796     invalidClass : 'has-warning',
29797     validClass : 'has-success',
29798     iconTooltip : 'This field is required',
29799     indicatorpos : 'left',
29800     
29801     getAutoCreate : function(){
29802         
29803         var cfg = {
29804             tag : this.tag,
29805             cls : 'roo-bootstrap-field-label ' + this.cls,
29806             for : this.target,
29807             cn : [
29808                 {
29809                     tag : 'i',
29810                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29811                     tooltip : this.iconTooltip
29812                 },
29813                 {
29814                     tag : 'span',
29815                     html : this.html
29816                 }
29817             ] 
29818         };
29819         
29820         if(this.indicatorpos == 'right'){
29821             var cfg = {
29822                 tag : this.tag,
29823                 cls : 'roo-bootstrap-field-label ' + this.cls,
29824                 for : this.target,
29825                 cn : [
29826                     {
29827                         tag : 'span',
29828                         html : this.html
29829                     },
29830                     {
29831                         tag : 'i',
29832                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29833                         tooltip : this.iconTooltip
29834                     }
29835                 ] 
29836             };
29837         }
29838         
29839         return cfg;
29840     },
29841     
29842     initEvents: function() 
29843     {
29844         Roo.bootstrap.Element.superclass.initEvents.call(this);
29845         
29846         this.indicator = this.indicatorEl();
29847         
29848         if(this.indicator){
29849             this.indicator.removeClass('visible');
29850             this.indicator.addClass('invisible');
29851         }
29852         
29853         Roo.bootstrap.FieldLabel.register(this);
29854     },
29855     
29856     indicatorEl : function()
29857     {
29858         var indicator = this.el.select('i.roo-required-indicator',true).first();
29859         
29860         if(!indicator){
29861             return false;
29862         }
29863         
29864         return indicator;
29865         
29866     },
29867     
29868     /**
29869      * Mark this field as valid
29870      */
29871     markValid : function()
29872     {
29873         if(this.indicator){
29874             this.indicator.removeClass('visible');
29875             this.indicator.addClass('invisible');
29876         }
29877         
29878         this.el.removeClass(this.invalidClass);
29879         
29880         this.el.addClass(this.validClass);
29881         
29882         this.fireEvent('valid', this);
29883     },
29884     
29885     /**
29886      * Mark this field as invalid
29887      * @param {String} msg The validation message
29888      */
29889     markInvalid : function(msg)
29890     {
29891         if(this.indicator){
29892             this.indicator.removeClass('invisible');
29893             this.indicator.addClass('visible');
29894         }
29895         
29896         this.el.removeClass(this.validClass);
29897         
29898         this.el.addClass(this.invalidClass);
29899         
29900         this.fireEvent('invalid', this, msg);
29901     }
29902     
29903    
29904 });
29905
29906 Roo.apply(Roo.bootstrap.FieldLabel, {
29907     
29908     groups: {},
29909     
29910      /**
29911     * register a FieldLabel Group
29912     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29913     */
29914     register : function(label)
29915     {
29916         if(this.groups.hasOwnProperty(label.target)){
29917             return;
29918         }
29919      
29920         this.groups[label.target] = label;
29921         
29922     },
29923     /**
29924     * fetch a FieldLabel Group based on the target
29925     * @param {string} target
29926     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29927     */
29928     get: function(target) {
29929         if (typeof(this.groups[target]) == 'undefined') {
29930             return false;
29931         }
29932         
29933         return this.groups[target] ;
29934     }
29935 });
29936
29937  
29938
29939  /*
29940  * - LGPL
29941  *
29942  * page DateSplitField.
29943  * 
29944  */
29945
29946
29947 /**
29948  * @class Roo.bootstrap.DateSplitField
29949  * @extends Roo.bootstrap.Component
29950  * Bootstrap DateSplitField class
29951  * @cfg {string} fieldLabel - the label associated
29952  * @cfg {Number} labelWidth set the width of label (0-12)
29953  * @cfg {String} labelAlign (top|left)
29954  * @cfg {Boolean} dayAllowBlank (true|false) default false
29955  * @cfg {Boolean} monthAllowBlank (true|false) default false
29956  * @cfg {Boolean} yearAllowBlank (true|false) default false
29957  * @cfg {string} dayPlaceholder 
29958  * @cfg {string} monthPlaceholder
29959  * @cfg {string} yearPlaceholder
29960  * @cfg {string} dayFormat default 'd'
29961  * @cfg {string} monthFormat default 'm'
29962  * @cfg {string} yearFormat default 'Y'
29963  * @cfg {Number} labellg set the width of label (1-12)
29964  * @cfg {Number} labelmd set the width of label (1-12)
29965  * @cfg {Number} labelsm set the width of label (1-12)
29966  * @cfg {Number} labelxs set the width of label (1-12)
29967
29968  *     
29969  * @constructor
29970  * Create a new DateSplitField
29971  * @param {Object} config The config object
29972  */
29973
29974 Roo.bootstrap.DateSplitField = function(config){
29975     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29976     
29977     this.addEvents({
29978         // raw events
29979          /**
29980          * @event years
29981          * getting the data of years
29982          * @param {Roo.bootstrap.DateSplitField} this
29983          * @param {Object} years
29984          */
29985         "years" : true,
29986         /**
29987          * @event days
29988          * getting the data of days
29989          * @param {Roo.bootstrap.DateSplitField} this
29990          * @param {Object} days
29991          */
29992         "days" : true,
29993         /**
29994          * @event invalid
29995          * Fires after the field has been marked as invalid.
29996          * @param {Roo.form.Field} this
29997          * @param {String} msg The validation message
29998          */
29999         invalid : true,
30000        /**
30001          * @event valid
30002          * Fires after the field has been validated with no errors.
30003          * @param {Roo.form.Field} this
30004          */
30005         valid : true
30006     });
30007 };
30008
30009 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30010     
30011     fieldLabel : '',
30012     labelAlign : 'top',
30013     labelWidth : 3,
30014     dayAllowBlank : false,
30015     monthAllowBlank : false,
30016     yearAllowBlank : false,
30017     dayPlaceholder : '',
30018     monthPlaceholder : '',
30019     yearPlaceholder : '',
30020     dayFormat : 'd',
30021     monthFormat : 'm',
30022     yearFormat : 'Y',
30023     isFormField : true,
30024     labellg : 0,
30025     labelmd : 0,
30026     labelsm : 0,
30027     labelxs : 0,
30028     
30029     getAutoCreate : function()
30030     {
30031         var cfg = {
30032             tag : 'div',
30033             cls : 'row roo-date-split-field-group',
30034             cn : [
30035                 {
30036                     tag : 'input',
30037                     type : 'hidden',
30038                     cls : 'form-hidden-field roo-date-split-field-group-value',
30039                     name : this.name
30040                 }
30041             ]
30042         };
30043         
30044         var labelCls = 'col-md-12';
30045         var contentCls = 'col-md-4';
30046         
30047         if(this.fieldLabel){
30048             
30049             var label = {
30050                 tag : 'div',
30051                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30052                 cn : [
30053                     {
30054                         tag : 'label',
30055                         html : this.fieldLabel
30056                     }
30057                 ]
30058             };
30059             
30060             if(this.labelAlign == 'left'){
30061             
30062                 if(this.labelWidth > 12){
30063                     label.style = "width: " + this.labelWidth + 'px';
30064                 }
30065
30066                 if(this.labelWidth < 13 && this.labelmd == 0){
30067                     this.labelmd = this.labelWidth;
30068                 }
30069
30070                 if(this.labellg > 0){
30071                     labelCls = ' col-lg-' + this.labellg;
30072                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30073                 }
30074
30075                 if(this.labelmd > 0){
30076                     labelCls = ' col-md-' + this.labelmd;
30077                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30078                 }
30079
30080                 if(this.labelsm > 0){
30081                     labelCls = ' col-sm-' + this.labelsm;
30082                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30083                 }
30084
30085                 if(this.labelxs > 0){
30086                     labelCls = ' col-xs-' + this.labelxs;
30087                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30088                 }
30089             }
30090             
30091             label.cls += ' ' + labelCls;
30092             
30093             cfg.cn.push(label);
30094         }
30095         
30096         Roo.each(['day', 'month', 'year'], function(t){
30097             cfg.cn.push({
30098                 tag : 'div',
30099                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30100             });
30101         }, this);
30102         
30103         return cfg;
30104     },
30105     
30106     inputEl: function ()
30107     {
30108         return this.el.select('.roo-date-split-field-group-value', true).first();
30109     },
30110     
30111     onRender : function(ct, position) 
30112     {
30113         var _this = this;
30114         
30115         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30116         
30117         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30118         
30119         this.dayField = new Roo.bootstrap.ComboBox({
30120             allowBlank : this.dayAllowBlank,
30121             alwaysQuery : true,
30122             displayField : 'value',
30123             editable : false,
30124             fieldLabel : '',
30125             forceSelection : true,
30126             mode : 'local',
30127             placeholder : this.dayPlaceholder,
30128             selectOnFocus : true,
30129             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30130             triggerAction : 'all',
30131             typeAhead : true,
30132             valueField : 'value',
30133             store : new Roo.data.SimpleStore({
30134                 data : (function() {    
30135                     var days = [];
30136                     _this.fireEvent('days', _this, days);
30137                     return days;
30138                 })(),
30139                 fields : [ 'value' ]
30140             }),
30141             listeners : {
30142                 select : function (_self, record, index)
30143                 {
30144                     _this.setValue(_this.getValue());
30145                 }
30146             }
30147         });
30148
30149         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30150         
30151         this.monthField = new Roo.bootstrap.MonthField({
30152             after : '<i class=\"fa fa-calendar\"></i>',
30153             allowBlank : this.monthAllowBlank,
30154             placeholder : this.monthPlaceholder,
30155             readOnly : true,
30156             listeners : {
30157                 render : function (_self)
30158                 {
30159                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30160                         e.preventDefault();
30161                         _self.focus();
30162                     });
30163                 },
30164                 select : function (_self, oldvalue, newvalue)
30165                 {
30166                     _this.setValue(_this.getValue());
30167                 }
30168             }
30169         });
30170         
30171         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30172         
30173         this.yearField = new Roo.bootstrap.ComboBox({
30174             allowBlank : this.yearAllowBlank,
30175             alwaysQuery : true,
30176             displayField : 'value',
30177             editable : false,
30178             fieldLabel : '',
30179             forceSelection : true,
30180             mode : 'local',
30181             placeholder : this.yearPlaceholder,
30182             selectOnFocus : true,
30183             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30184             triggerAction : 'all',
30185             typeAhead : true,
30186             valueField : 'value',
30187             store : new Roo.data.SimpleStore({
30188                 data : (function() {
30189                     var years = [];
30190                     _this.fireEvent('years', _this, years);
30191                     return years;
30192                 })(),
30193                 fields : [ 'value' ]
30194             }),
30195             listeners : {
30196                 select : function (_self, record, index)
30197                 {
30198                     _this.setValue(_this.getValue());
30199                 }
30200             }
30201         });
30202
30203         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30204     },
30205     
30206     setValue : function(v, format)
30207     {
30208         this.inputEl.dom.value = v;
30209         
30210         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30211         
30212         var d = Date.parseDate(v, f);
30213         
30214         if(!d){
30215             this.validate();
30216             return;
30217         }
30218         
30219         this.setDay(d.format(this.dayFormat));
30220         this.setMonth(d.format(this.monthFormat));
30221         this.setYear(d.format(this.yearFormat));
30222         
30223         this.validate();
30224         
30225         return;
30226     },
30227     
30228     setDay : function(v)
30229     {
30230         this.dayField.setValue(v);
30231         this.inputEl.dom.value = this.getValue();
30232         this.validate();
30233         return;
30234     },
30235     
30236     setMonth : function(v)
30237     {
30238         this.monthField.setValue(v, true);
30239         this.inputEl.dom.value = this.getValue();
30240         this.validate();
30241         return;
30242     },
30243     
30244     setYear : function(v)
30245     {
30246         this.yearField.setValue(v);
30247         this.inputEl.dom.value = this.getValue();
30248         this.validate();
30249         return;
30250     },
30251     
30252     getDay : function()
30253     {
30254         return this.dayField.getValue();
30255     },
30256     
30257     getMonth : function()
30258     {
30259         return this.monthField.getValue();
30260     },
30261     
30262     getYear : function()
30263     {
30264         return this.yearField.getValue();
30265     },
30266     
30267     getValue : function()
30268     {
30269         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30270         
30271         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30272         
30273         return date;
30274     },
30275     
30276     reset : function()
30277     {
30278         this.setDay('');
30279         this.setMonth('');
30280         this.setYear('');
30281         this.inputEl.dom.value = '';
30282         this.validate();
30283         return;
30284     },
30285     
30286     validate : function()
30287     {
30288         var d = this.dayField.validate();
30289         var m = this.monthField.validate();
30290         var y = this.yearField.validate();
30291         
30292         var valid = true;
30293         
30294         if(
30295                 (!this.dayAllowBlank && !d) ||
30296                 (!this.monthAllowBlank && !m) ||
30297                 (!this.yearAllowBlank && !y)
30298         ){
30299             valid = false;
30300         }
30301         
30302         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30303             return valid;
30304         }
30305         
30306         if(valid){
30307             this.markValid();
30308             return valid;
30309         }
30310         
30311         this.markInvalid();
30312         
30313         return valid;
30314     },
30315     
30316     markValid : function()
30317     {
30318         
30319         var label = this.el.select('label', true).first();
30320         var icon = this.el.select('i.fa-star', true).first();
30321
30322         if(label && icon){
30323             icon.remove();
30324         }
30325         
30326         this.fireEvent('valid', this);
30327     },
30328     
30329      /**
30330      * Mark this field as invalid
30331      * @param {String} msg The validation message
30332      */
30333     markInvalid : function(msg)
30334     {
30335         
30336         var label = this.el.select('label', true).first();
30337         var icon = this.el.select('i.fa-star', true).first();
30338
30339         if(label && !icon){
30340             this.el.select('.roo-date-split-field-label', true).createChild({
30341                 tag : 'i',
30342                 cls : 'text-danger fa fa-lg fa-star',
30343                 tooltip : 'This field is required',
30344                 style : 'margin-right:5px;'
30345             }, label, true);
30346         }
30347         
30348         this.fireEvent('invalid', this, msg);
30349     },
30350     
30351     clearInvalid : function()
30352     {
30353         var label = this.el.select('label', true).first();
30354         var icon = this.el.select('i.fa-star', true).first();
30355
30356         if(label && icon){
30357             icon.remove();
30358         }
30359         
30360         this.fireEvent('valid', this);
30361     },
30362     
30363     getName: function()
30364     {
30365         return this.name;
30366     }
30367     
30368 });
30369
30370  /**
30371  *
30372  * This is based on 
30373  * http://masonry.desandro.com
30374  *
30375  * The idea is to render all the bricks based on vertical width...
30376  *
30377  * The original code extends 'outlayer' - we might need to use that....
30378  * 
30379  */
30380
30381
30382 /**
30383  * @class Roo.bootstrap.LayoutMasonry
30384  * @extends Roo.bootstrap.Component
30385  * Bootstrap Layout Masonry class
30386  * 
30387  * @constructor
30388  * Create a new Element
30389  * @param {Object} config The config object
30390  */
30391
30392 Roo.bootstrap.LayoutMasonry = function(config){
30393     
30394     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30395     
30396     this.bricks = [];
30397     
30398     Roo.bootstrap.LayoutMasonry.register(this);
30399     
30400     this.addEvents({
30401         // raw events
30402         /**
30403          * @event layout
30404          * Fire after layout the items
30405          * @param {Roo.bootstrap.LayoutMasonry} this
30406          * @param {Roo.EventObject} e
30407          */
30408         "layout" : true
30409     });
30410     
30411 };
30412
30413 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30414     
30415     /**
30416      * @cfg {Boolean} isLayoutInstant = no animation?
30417      */   
30418     isLayoutInstant : false, // needed?
30419    
30420     /**
30421      * @cfg {Number} boxWidth  width of the columns
30422      */   
30423     boxWidth : 450,
30424     
30425       /**
30426      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30427      */   
30428     boxHeight : 0,
30429     
30430     /**
30431      * @cfg {Number} padWidth padding below box..
30432      */   
30433     padWidth : 10, 
30434     
30435     /**
30436      * @cfg {Number} gutter gutter width..
30437      */   
30438     gutter : 10,
30439     
30440      /**
30441      * @cfg {Number} maxCols maximum number of columns
30442      */   
30443     
30444     maxCols: 0,
30445     
30446     /**
30447      * @cfg {Boolean} isAutoInitial defalut true
30448      */   
30449     isAutoInitial : true, 
30450     
30451     containerWidth: 0,
30452     
30453     /**
30454      * @cfg {Boolean} isHorizontal defalut false
30455      */   
30456     isHorizontal : false, 
30457
30458     currentSize : null,
30459     
30460     tag: 'div',
30461     
30462     cls: '',
30463     
30464     bricks: null, //CompositeElement
30465     
30466     cols : 1,
30467     
30468     _isLayoutInited : false,
30469     
30470 //    isAlternative : false, // only use for vertical layout...
30471     
30472     /**
30473      * @cfg {Number} alternativePadWidth padding below box..
30474      */   
30475     alternativePadWidth : 50,
30476     
30477     selectedBrick : [],
30478     
30479     getAutoCreate : function(){
30480         
30481         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30482         
30483         var cfg = {
30484             tag: this.tag,
30485             cls: 'blog-masonary-wrapper ' + this.cls,
30486             cn : {
30487                 cls : 'mas-boxes masonary'
30488             }
30489         };
30490         
30491         return cfg;
30492     },
30493     
30494     getChildContainer: function( )
30495     {
30496         if (this.boxesEl) {
30497             return this.boxesEl;
30498         }
30499         
30500         this.boxesEl = this.el.select('.mas-boxes').first();
30501         
30502         return this.boxesEl;
30503     },
30504     
30505     
30506     initEvents : function()
30507     {
30508         var _this = this;
30509         
30510         if(this.isAutoInitial){
30511             Roo.log('hook children rendered');
30512             this.on('childrenrendered', function() {
30513                 Roo.log('children rendered');
30514                 _this.initial();
30515             } ,this);
30516         }
30517     },
30518     
30519     initial : function()
30520     {
30521         this.selectedBrick = [];
30522         
30523         this.currentSize = this.el.getBox(true);
30524         
30525         Roo.EventManager.onWindowResize(this.resize, this); 
30526
30527         if(!this.isAutoInitial){
30528             this.layout();
30529             return;
30530         }
30531         
30532         this.layout();
30533         
30534         return;
30535         //this.layout.defer(500,this);
30536         
30537     },
30538     
30539     resize : function()
30540     {
30541         var cs = this.el.getBox(true);
30542         
30543         if (
30544                 this.currentSize.width == cs.width && 
30545                 this.currentSize.x == cs.x && 
30546                 this.currentSize.height == cs.height && 
30547                 this.currentSize.y == cs.y 
30548         ) {
30549             Roo.log("no change in with or X or Y");
30550             return;
30551         }
30552         
30553         this.currentSize = cs;
30554         
30555         this.layout();
30556         
30557     },
30558     
30559     layout : function()
30560     {   
30561         this._resetLayout();
30562         
30563         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30564         
30565         this.layoutItems( isInstant );
30566       
30567         this._isLayoutInited = true;
30568         
30569         this.fireEvent('layout', this);
30570         
30571     },
30572     
30573     _resetLayout : function()
30574     {
30575         if(this.isHorizontal){
30576             this.horizontalMeasureColumns();
30577             return;
30578         }
30579         
30580         this.verticalMeasureColumns();
30581         
30582     },
30583     
30584     verticalMeasureColumns : function()
30585     {
30586         this.getContainerWidth();
30587         
30588 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30589 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30590 //            return;
30591 //        }
30592         
30593         var boxWidth = this.boxWidth + this.padWidth;
30594         
30595         if(this.containerWidth < this.boxWidth){
30596             boxWidth = this.containerWidth
30597         }
30598         
30599         var containerWidth = this.containerWidth;
30600         
30601         var cols = Math.floor(containerWidth / boxWidth);
30602         
30603         this.cols = Math.max( cols, 1 );
30604         
30605         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30606         
30607         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30608         
30609         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30610         
30611         this.colWidth = boxWidth + avail - this.padWidth;
30612         
30613         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30614         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30615     },
30616     
30617     horizontalMeasureColumns : function()
30618     {
30619         this.getContainerWidth();
30620         
30621         var boxWidth = this.boxWidth;
30622         
30623         if(this.containerWidth < boxWidth){
30624             boxWidth = this.containerWidth;
30625         }
30626         
30627         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30628         
30629         this.el.setHeight(boxWidth);
30630         
30631     },
30632     
30633     getContainerWidth : function()
30634     {
30635         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30636     },
30637     
30638     layoutItems : function( isInstant )
30639     {
30640         Roo.log(this.bricks);
30641         
30642         var items = Roo.apply([], this.bricks);
30643         
30644         if(this.isHorizontal){
30645             this._horizontalLayoutItems( items , isInstant );
30646             return;
30647         }
30648         
30649 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30650 //            this._verticalAlternativeLayoutItems( items , isInstant );
30651 //            return;
30652 //        }
30653         
30654         this._verticalLayoutItems( items , isInstant );
30655         
30656     },
30657     
30658     _verticalLayoutItems : function ( items , isInstant)
30659     {
30660         if ( !items || !items.length ) {
30661             return;
30662         }
30663         
30664         var standard = [
30665             ['xs', 'xs', 'xs', 'tall'],
30666             ['xs', 'xs', 'tall'],
30667             ['xs', 'xs', 'sm'],
30668             ['xs', 'xs', 'xs'],
30669             ['xs', 'tall'],
30670             ['xs', 'sm'],
30671             ['xs', 'xs'],
30672             ['xs'],
30673             
30674             ['sm', 'xs', 'xs'],
30675             ['sm', 'xs'],
30676             ['sm'],
30677             
30678             ['tall', 'xs', 'xs', 'xs'],
30679             ['tall', 'xs', 'xs'],
30680             ['tall', 'xs'],
30681             ['tall']
30682             
30683         ];
30684         
30685         var queue = [];
30686         
30687         var boxes = [];
30688         
30689         var box = [];
30690         
30691         Roo.each(items, function(item, k){
30692             
30693             switch (item.size) {
30694                 // these layouts take up a full box,
30695                 case 'md' :
30696                 case 'md-left' :
30697                 case 'md-right' :
30698                 case 'wide' :
30699                     
30700                     if(box.length){
30701                         boxes.push(box);
30702                         box = [];
30703                     }
30704                     
30705                     boxes.push([item]);
30706                     
30707                     break;
30708                     
30709                 case 'xs' :
30710                 case 'sm' :
30711                 case 'tall' :
30712                     
30713                     box.push(item);
30714                     
30715                     break;
30716                 default :
30717                     break;
30718                     
30719             }
30720             
30721         }, this);
30722         
30723         if(box.length){
30724             boxes.push(box);
30725             box = [];
30726         }
30727         
30728         var filterPattern = function(box, length)
30729         {
30730             if(!box.length){
30731                 return;
30732             }
30733             
30734             var match = false;
30735             
30736             var pattern = box.slice(0, length);
30737             
30738             var format = [];
30739             
30740             Roo.each(pattern, function(i){
30741                 format.push(i.size);
30742             }, this);
30743             
30744             Roo.each(standard, function(s){
30745                 
30746                 if(String(s) != String(format)){
30747                     return;
30748                 }
30749                 
30750                 match = true;
30751                 return false;
30752                 
30753             }, this);
30754             
30755             if(!match && length == 1){
30756                 return;
30757             }
30758             
30759             if(!match){
30760                 filterPattern(box, length - 1);
30761                 return;
30762             }
30763                 
30764             queue.push(pattern);
30765
30766             box = box.slice(length, box.length);
30767
30768             filterPattern(box, 4);
30769
30770             return;
30771             
30772         }
30773         
30774         Roo.each(boxes, function(box, k){
30775             
30776             if(!box.length){
30777                 return;
30778             }
30779             
30780             if(box.length == 1){
30781                 queue.push(box);
30782                 return;
30783             }
30784             
30785             filterPattern(box, 4);
30786             
30787         }, this);
30788         
30789         this._processVerticalLayoutQueue( queue, isInstant );
30790         
30791     },
30792     
30793 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30794 //    {
30795 //        if ( !items || !items.length ) {
30796 //            return;
30797 //        }
30798 //
30799 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30800 //        
30801 //    },
30802     
30803     _horizontalLayoutItems : function ( items , isInstant)
30804     {
30805         if ( !items || !items.length || items.length < 3) {
30806             return;
30807         }
30808         
30809         items.reverse();
30810         
30811         var eItems = items.slice(0, 3);
30812         
30813         items = items.slice(3, items.length);
30814         
30815         var standard = [
30816             ['xs', 'xs', 'xs', 'wide'],
30817             ['xs', 'xs', 'wide'],
30818             ['xs', 'xs', 'sm'],
30819             ['xs', 'xs', 'xs'],
30820             ['xs', 'wide'],
30821             ['xs', 'sm'],
30822             ['xs', 'xs'],
30823             ['xs'],
30824             
30825             ['sm', 'xs', 'xs'],
30826             ['sm', 'xs'],
30827             ['sm'],
30828             
30829             ['wide', 'xs', 'xs', 'xs'],
30830             ['wide', 'xs', 'xs'],
30831             ['wide', 'xs'],
30832             ['wide'],
30833             
30834             ['wide-thin']
30835         ];
30836         
30837         var queue = [];
30838         
30839         var boxes = [];
30840         
30841         var box = [];
30842         
30843         Roo.each(items, function(item, k){
30844             
30845             switch (item.size) {
30846                 case 'md' :
30847                 case 'md-left' :
30848                 case 'md-right' :
30849                 case 'tall' :
30850                     
30851                     if(box.length){
30852                         boxes.push(box);
30853                         box = [];
30854                     }
30855                     
30856                     boxes.push([item]);
30857                     
30858                     break;
30859                     
30860                 case 'xs' :
30861                 case 'sm' :
30862                 case 'wide' :
30863                 case 'wide-thin' :
30864                     
30865                     box.push(item);
30866                     
30867                     break;
30868                 default :
30869                     break;
30870                     
30871             }
30872             
30873         }, this);
30874         
30875         if(box.length){
30876             boxes.push(box);
30877             box = [];
30878         }
30879         
30880         var filterPattern = function(box, length)
30881         {
30882             if(!box.length){
30883                 return;
30884             }
30885             
30886             var match = false;
30887             
30888             var pattern = box.slice(0, length);
30889             
30890             var format = [];
30891             
30892             Roo.each(pattern, function(i){
30893                 format.push(i.size);
30894             }, this);
30895             
30896             Roo.each(standard, function(s){
30897                 
30898                 if(String(s) != String(format)){
30899                     return;
30900                 }
30901                 
30902                 match = true;
30903                 return false;
30904                 
30905             }, this);
30906             
30907             if(!match && length == 1){
30908                 return;
30909             }
30910             
30911             if(!match){
30912                 filterPattern(box, length - 1);
30913                 return;
30914             }
30915                 
30916             queue.push(pattern);
30917
30918             box = box.slice(length, box.length);
30919
30920             filterPattern(box, 4);
30921
30922             return;
30923             
30924         }
30925         
30926         Roo.each(boxes, function(box, k){
30927             
30928             if(!box.length){
30929                 return;
30930             }
30931             
30932             if(box.length == 1){
30933                 queue.push(box);
30934                 return;
30935             }
30936             
30937             filterPattern(box, 4);
30938             
30939         }, this);
30940         
30941         
30942         var prune = [];
30943         
30944         var pos = this.el.getBox(true);
30945         
30946         var minX = pos.x;
30947         
30948         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30949         
30950         var hit_end = false;
30951         
30952         Roo.each(queue, function(box){
30953             
30954             if(hit_end){
30955                 
30956                 Roo.each(box, function(b){
30957                 
30958                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30959                     b.el.hide();
30960
30961                 }, this);
30962
30963                 return;
30964             }
30965             
30966             var mx = 0;
30967             
30968             Roo.each(box, function(b){
30969                 
30970                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30971                 b.el.show();
30972
30973                 mx = Math.max(mx, b.x);
30974                 
30975             }, this);
30976             
30977             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30978             
30979             if(maxX < minX){
30980                 
30981                 Roo.each(box, function(b){
30982                 
30983                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30984                     b.el.hide();
30985                     
30986                 }, this);
30987                 
30988                 hit_end = true;
30989                 
30990                 return;
30991             }
30992             
30993             prune.push(box);
30994             
30995         }, this);
30996         
30997         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30998     },
30999     
31000     /** Sets position of item in DOM
31001     * @param {Element} item
31002     * @param {Number} x - horizontal position
31003     * @param {Number} y - vertical position
31004     * @param {Boolean} isInstant - disables transitions
31005     */
31006     _processVerticalLayoutQueue : function( queue, isInstant )
31007     {
31008         var pos = this.el.getBox(true);
31009         var x = pos.x;
31010         var y = pos.y;
31011         var maxY = [];
31012         
31013         for (var i = 0; i < this.cols; i++){
31014             maxY[i] = pos.y;
31015         }
31016         
31017         Roo.each(queue, function(box, k){
31018             
31019             var col = k % this.cols;
31020             
31021             Roo.each(box, function(b,kk){
31022                 
31023                 b.el.position('absolute');
31024                 
31025                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31026                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31027                 
31028                 if(b.size == 'md-left' || b.size == 'md-right'){
31029                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31030                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31031                 }
31032                 
31033                 b.el.setWidth(width);
31034                 b.el.setHeight(height);
31035                 // iframe?
31036                 b.el.select('iframe',true).setSize(width,height);
31037                 
31038             }, this);
31039             
31040             for (var i = 0; i < this.cols; i++){
31041                 
31042                 if(maxY[i] < maxY[col]){
31043                     col = i;
31044                     continue;
31045                 }
31046                 
31047                 col = Math.min(col, i);
31048                 
31049             }
31050             
31051             x = pos.x + col * (this.colWidth + this.padWidth);
31052             
31053             y = maxY[col];
31054             
31055             var positions = [];
31056             
31057             switch (box.length){
31058                 case 1 :
31059                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31060                     break;
31061                 case 2 :
31062                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31063                     break;
31064                 case 3 :
31065                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31066                     break;
31067                 case 4 :
31068                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31069                     break;
31070                 default :
31071                     break;
31072             }
31073             
31074             Roo.each(box, function(b,kk){
31075                 
31076                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31077                 
31078                 var sz = b.el.getSize();
31079                 
31080                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31081                 
31082             }, this);
31083             
31084         }, this);
31085         
31086         var mY = 0;
31087         
31088         for (var i = 0; i < this.cols; i++){
31089             mY = Math.max(mY, maxY[i]);
31090         }
31091         
31092         this.el.setHeight(mY - pos.y);
31093         
31094     },
31095     
31096 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31097 //    {
31098 //        var pos = this.el.getBox(true);
31099 //        var x = pos.x;
31100 //        var y = pos.y;
31101 //        var maxX = pos.right;
31102 //        
31103 //        var maxHeight = 0;
31104 //        
31105 //        Roo.each(items, function(item, k){
31106 //            
31107 //            var c = k % 2;
31108 //            
31109 //            item.el.position('absolute');
31110 //                
31111 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31112 //
31113 //            item.el.setWidth(width);
31114 //
31115 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31116 //
31117 //            item.el.setHeight(height);
31118 //            
31119 //            if(c == 0){
31120 //                item.el.setXY([x, y], isInstant ? false : true);
31121 //            } else {
31122 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31123 //            }
31124 //            
31125 //            y = y + height + this.alternativePadWidth;
31126 //            
31127 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31128 //            
31129 //        }, this);
31130 //        
31131 //        this.el.setHeight(maxHeight);
31132 //        
31133 //    },
31134     
31135     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31136     {
31137         var pos = this.el.getBox(true);
31138         
31139         var minX = pos.x;
31140         var minY = pos.y;
31141         
31142         var maxX = pos.right;
31143         
31144         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31145         
31146         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31147         
31148         Roo.each(queue, function(box, k){
31149             
31150             Roo.each(box, function(b, kk){
31151                 
31152                 b.el.position('absolute');
31153                 
31154                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31155                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31156                 
31157                 if(b.size == 'md-left' || b.size == 'md-right'){
31158                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31159                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31160                 }
31161                 
31162                 b.el.setWidth(width);
31163                 b.el.setHeight(height);
31164                 
31165             }, this);
31166             
31167             if(!box.length){
31168                 return;
31169             }
31170             
31171             var positions = [];
31172             
31173             switch (box.length){
31174                 case 1 :
31175                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31176                     break;
31177                 case 2 :
31178                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31179                     break;
31180                 case 3 :
31181                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31182                     break;
31183                 case 4 :
31184                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31185                     break;
31186                 default :
31187                     break;
31188             }
31189             
31190             Roo.each(box, function(b,kk){
31191                 
31192                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31193                 
31194                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31195                 
31196             }, this);
31197             
31198         }, this);
31199         
31200     },
31201     
31202     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31203     {
31204         Roo.each(eItems, function(b,k){
31205             
31206             b.size = (k == 0) ? 'sm' : 'xs';
31207             b.x = (k == 0) ? 2 : 1;
31208             b.y = (k == 0) ? 2 : 1;
31209             
31210             b.el.position('absolute');
31211             
31212             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31213                 
31214             b.el.setWidth(width);
31215             
31216             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31217             
31218             b.el.setHeight(height);
31219             
31220         }, this);
31221
31222         var positions = [];
31223         
31224         positions.push({
31225             x : maxX - this.unitWidth * 2 - this.gutter,
31226             y : minY
31227         });
31228         
31229         positions.push({
31230             x : maxX - this.unitWidth,
31231             y : minY + (this.unitWidth + this.gutter) * 2
31232         });
31233         
31234         positions.push({
31235             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31236             y : minY
31237         });
31238         
31239         Roo.each(eItems, function(b,k){
31240             
31241             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31242
31243         }, this);
31244         
31245     },
31246     
31247     getVerticalOneBoxColPositions : function(x, y, box)
31248     {
31249         var pos = [];
31250         
31251         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31252         
31253         if(box[0].size == 'md-left'){
31254             rand = 0;
31255         }
31256         
31257         if(box[0].size == 'md-right'){
31258             rand = 1;
31259         }
31260         
31261         pos.push({
31262             x : x + (this.unitWidth + this.gutter) * rand,
31263             y : y
31264         });
31265         
31266         return pos;
31267     },
31268     
31269     getVerticalTwoBoxColPositions : function(x, y, box)
31270     {
31271         var pos = [];
31272         
31273         if(box[0].size == 'xs'){
31274             
31275             pos.push({
31276                 x : x,
31277                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31278             });
31279
31280             pos.push({
31281                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31282                 y : y
31283             });
31284             
31285             return pos;
31286             
31287         }
31288         
31289         pos.push({
31290             x : x,
31291             y : y
31292         });
31293
31294         pos.push({
31295             x : x + (this.unitWidth + this.gutter) * 2,
31296             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31297         });
31298         
31299         return pos;
31300         
31301     },
31302     
31303     getVerticalThreeBoxColPositions : function(x, y, box)
31304     {
31305         var pos = [];
31306         
31307         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31308             
31309             pos.push({
31310                 x : x,
31311                 y : y
31312             });
31313
31314             pos.push({
31315                 x : x + (this.unitWidth + this.gutter) * 1,
31316                 y : y
31317             });
31318             
31319             pos.push({
31320                 x : x + (this.unitWidth + this.gutter) * 2,
31321                 y : y
31322             });
31323             
31324             return pos;
31325             
31326         }
31327         
31328         if(box[0].size == 'xs' && box[1].size == 'xs'){
31329             
31330             pos.push({
31331                 x : x,
31332                 y : y
31333             });
31334
31335             pos.push({
31336                 x : x,
31337                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31338             });
31339             
31340             pos.push({
31341                 x : x + (this.unitWidth + this.gutter) * 1,
31342                 y : y
31343             });
31344             
31345             return pos;
31346             
31347         }
31348         
31349         pos.push({
31350             x : x,
31351             y : y
31352         });
31353
31354         pos.push({
31355             x : x + (this.unitWidth + this.gutter) * 2,
31356             y : y
31357         });
31358
31359         pos.push({
31360             x : x + (this.unitWidth + this.gutter) * 2,
31361             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31362         });
31363             
31364         return pos;
31365         
31366     },
31367     
31368     getVerticalFourBoxColPositions : function(x, y, box)
31369     {
31370         var pos = [];
31371         
31372         if(box[0].size == 'xs'){
31373             
31374             pos.push({
31375                 x : x,
31376                 y : y
31377             });
31378
31379             pos.push({
31380                 x : x,
31381                 y : y + (this.unitHeight + this.gutter) * 1
31382             });
31383             
31384             pos.push({
31385                 x : x,
31386                 y : y + (this.unitHeight + this.gutter) * 2
31387             });
31388             
31389             pos.push({
31390                 x : x + (this.unitWidth + this.gutter) * 1,
31391                 y : y
31392             });
31393             
31394             return pos;
31395             
31396         }
31397         
31398         pos.push({
31399             x : x,
31400             y : y
31401         });
31402
31403         pos.push({
31404             x : x + (this.unitWidth + this.gutter) * 2,
31405             y : y
31406         });
31407
31408         pos.push({
31409             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31410             y : y + (this.unitHeight + this.gutter) * 1
31411         });
31412
31413         pos.push({
31414             x : x + (this.unitWidth + this.gutter) * 2,
31415             y : y + (this.unitWidth + this.gutter) * 2
31416         });
31417
31418         return pos;
31419         
31420     },
31421     
31422     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31423     {
31424         var pos = [];
31425         
31426         if(box[0].size == 'md-left'){
31427             pos.push({
31428                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31429                 y : minY
31430             });
31431             
31432             return pos;
31433         }
31434         
31435         if(box[0].size == 'md-right'){
31436             pos.push({
31437                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31438                 y : minY + (this.unitWidth + this.gutter) * 1
31439             });
31440             
31441             return pos;
31442         }
31443         
31444         var rand = Math.floor(Math.random() * (4 - box[0].y));
31445         
31446         pos.push({
31447             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31448             y : minY + (this.unitWidth + this.gutter) * rand
31449         });
31450         
31451         return pos;
31452         
31453     },
31454     
31455     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31456     {
31457         var pos = [];
31458         
31459         if(box[0].size == 'xs'){
31460             
31461             pos.push({
31462                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31463                 y : minY
31464             });
31465
31466             pos.push({
31467                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31468                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31469             });
31470             
31471             return pos;
31472             
31473         }
31474         
31475         pos.push({
31476             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31477             y : minY
31478         });
31479
31480         pos.push({
31481             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31482             y : minY + (this.unitWidth + this.gutter) * 2
31483         });
31484         
31485         return pos;
31486         
31487     },
31488     
31489     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31490     {
31491         var pos = [];
31492         
31493         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31494             
31495             pos.push({
31496                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31497                 y : minY
31498             });
31499
31500             pos.push({
31501                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31502                 y : minY + (this.unitWidth + this.gutter) * 1
31503             });
31504             
31505             pos.push({
31506                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31507                 y : minY + (this.unitWidth + this.gutter) * 2
31508             });
31509             
31510             return pos;
31511             
31512         }
31513         
31514         if(box[0].size == 'xs' && box[1].size == 'xs'){
31515             
31516             pos.push({
31517                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31518                 y : minY
31519             });
31520
31521             pos.push({
31522                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31523                 y : minY
31524             });
31525             
31526             pos.push({
31527                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31528                 y : minY + (this.unitWidth + this.gutter) * 1
31529             });
31530             
31531             return pos;
31532             
31533         }
31534         
31535         pos.push({
31536             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31537             y : minY
31538         });
31539
31540         pos.push({
31541             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31542             y : minY + (this.unitWidth + this.gutter) * 2
31543         });
31544
31545         pos.push({
31546             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31547             y : minY + (this.unitWidth + this.gutter) * 2
31548         });
31549             
31550         return pos;
31551         
31552     },
31553     
31554     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31555     {
31556         var pos = [];
31557         
31558         if(box[0].size == 'xs'){
31559             
31560             pos.push({
31561                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31562                 y : minY
31563             });
31564
31565             pos.push({
31566                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31567                 y : minY
31568             });
31569             
31570             pos.push({
31571                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31572                 y : minY
31573             });
31574             
31575             pos.push({
31576                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31577                 y : minY + (this.unitWidth + this.gutter) * 1
31578             });
31579             
31580             return pos;
31581             
31582         }
31583         
31584         pos.push({
31585             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31586             y : minY
31587         });
31588         
31589         pos.push({
31590             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31591             y : minY + (this.unitWidth + this.gutter) * 2
31592         });
31593         
31594         pos.push({
31595             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31596             y : minY + (this.unitWidth + this.gutter) * 2
31597         });
31598         
31599         pos.push({
31600             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31601             y : minY + (this.unitWidth + this.gutter) * 2
31602         });
31603
31604         return pos;
31605         
31606     },
31607     
31608     /**
31609     * remove a Masonry Brick
31610     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31611     */
31612     removeBrick : function(brick_id)
31613     {
31614         if (!brick_id) {
31615             return;
31616         }
31617         
31618         for (var i = 0; i<this.bricks.length; i++) {
31619             if (this.bricks[i].id == brick_id) {
31620                 this.bricks.splice(i,1);
31621                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31622                 this.initial();
31623             }
31624         }
31625     },
31626     
31627     /**
31628     * adds a Masonry Brick
31629     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31630     */
31631     addBrick : function(cfg)
31632     {
31633         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31634         //this.register(cn);
31635         cn.parentId = this.id;
31636         cn.onRender(this.el, null);
31637         return cn;
31638     },
31639     
31640     /**
31641     * register a Masonry Brick
31642     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31643     */
31644     
31645     register : function(brick)
31646     {
31647         this.bricks.push(brick);
31648         brick.masonryId = this.id;
31649     },
31650     
31651     /**
31652     * clear all the Masonry Brick
31653     */
31654     clearAll : function()
31655     {
31656         this.bricks = [];
31657         //this.getChildContainer().dom.innerHTML = "";
31658         this.el.dom.innerHTML = '';
31659     },
31660     
31661     getSelected : function()
31662     {
31663         if (!this.selectedBrick) {
31664             return false;
31665         }
31666         
31667         return this.selectedBrick;
31668     }
31669 });
31670
31671 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31672     
31673     groups: {},
31674      /**
31675     * register a Masonry Layout
31676     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31677     */
31678     
31679     register : function(layout)
31680     {
31681         this.groups[layout.id] = layout;
31682     },
31683     /**
31684     * fetch a  Masonry Layout based on the masonry layout ID
31685     * @param {string} the masonry layout to add
31686     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31687     */
31688     
31689     get: function(layout_id) {
31690         if (typeof(this.groups[layout_id]) == 'undefined') {
31691             return false;
31692         }
31693         return this.groups[layout_id] ;
31694     }
31695     
31696     
31697     
31698 });
31699
31700  
31701
31702  /**
31703  *
31704  * This is based on 
31705  * http://masonry.desandro.com
31706  *
31707  * The idea is to render all the bricks based on vertical width...
31708  *
31709  * The original code extends 'outlayer' - we might need to use that....
31710  * 
31711  */
31712
31713
31714 /**
31715  * @class Roo.bootstrap.LayoutMasonryAuto
31716  * @extends Roo.bootstrap.Component
31717  * Bootstrap Layout Masonry class
31718  * 
31719  * @constructor
31720  * Create a new Element
31721  * @param {Object} config The config object
31722  */
31723
31724 Roo.bootstrap.LayoutMasonryAuto = function(config){
31725     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31726 };
31727
31728 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31729     
31730       /**
31731      * @cfg {Boolean} isFitWidth  - resize the width..
31732      */   
31733     isFitWidth : false,  // options..
31734     /**
31735      * @cfg {Boolean} isOriginLeft = left align?
31736      */   
31737     isOriginLeft : true,
31738     /**
31739      * @cfg {Boolean} isOriginTop = top align?
31740      */   
31741     isOriginTop : false,
31742     /**
31743      * @cfg {Boolean} isLayoutInstant = no animation?
31744      */   
31745     isLayoutInstant : false, // needed?
31746     /**
31747      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31748      */   
31749     isResizingContainer : true,
31750     /**
31751      * @cfg {Number} columnWidth  width of the columns 
31752      */   
31753     
31754     columnWidth : 0,
31755     
31756     /**
31757      * @cfg {Number} maxCols maximum number of columns
31758      */   
31759     
31760     maxCols: 0,
31761     /**
31762      * @cfg {Number} padHeight padding below box..
31763      */   
31764     
31765     padHeight : 10, 
31766     
31767     /**
31768      * @cfg {Boolean} isAutoInitial defalut true
31769      */   
31770     
31771     isAutoInitial : true, 
31772     
31773     // private?
31774     gutter : 0,
31775     
31776     containerWidth: 0,
31777     initialColumnWidth : 0,
31778     currentSize : null,
31779     
31780     colYs : null, // array.
31781     maxY : 0,
31782     padWidth: 10,
31783     
31784     
31785     tag: 'div',
31786     cls: '',
31787     bricks: null, //CompositeElement
31788     cols : 0, // array?
31789     // element : null, // wrapped now this.el
31790     _isLayoutInited : null, 
31791     
31792     
31793     getAutoCreate : function(){
31794         
31795         var cfg = {
31796             tag: this.tag,
31797             cls: 'blog-masonary-wrapper ' + this.cls,
31798             cn : {
31799                 cls : 'mas-boxes masonary'
31800             }
31801         };
31802         
31803         return cfg;
31804     },
31805     
31806     getChildContainer: function( )
31807     {
31808         if (this.boxesEl) {
31809             return this.boxesEl;
31810         }
31811         
31812         this.boxesEl = this.el.select('.mas-boxes').first();
31813         
31814         return this.boxesEl;
31815     },
31816     
31817     
31818     initEvents : function()
31819     {
31820         var _this = this;
31821         
31822         if(this.isAutoInitial){
31823             Roo.log('hook children rendered');
31824             this.on('childrenrendered', function() {
31825                 Roo.log('children rendered');
31826                 _this.initial();
31827             } ,this);
31828         }
31829         
31830     },
31831     
31832     initial : function()
31833     {
31834         this.reloadItems();
31835
31836         this.currentSize = this.el.getBox(true);
31837
31838         /// was window resize... - let's see if this works..
31839         Roo.EventManager.onWindowResize(this.resize, this); 
31840
31841         if(!this.isAutoInitial){
31842             this.layout();
31843             return;
31844         }
31845         
31846         this.layout.defer(500,this);
31847     },
31848     
31849     reloadItems: function()
31850     {
31851         this.bricks = this.el.select('.masonry-brick', true);
31852         
31853         this.bricks.each(function(b) {
31854             //Roo.log(b.getSize());
31855             if (!b.attr('originalwidth')) {
31856                 b.attr('originalwidth',  b.getSize().width);
31857             }
31858             
31859         });
31860         
31861         Roo.log(this.bricks.elements.length);
31862     },
31863     
31864     resize : function()
31865     {
31866         Roo.log('resize');
31867         var cs = this.el.getBox(true);
31868         
31869         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31870             Roo.log("no change in with or X");
31871             return;
31872         }
31873         this.currentSize = cs;
31874         this.layout();
31875     },
31876     
31877     layout : function()
31878     {
31879          Roo.log('layout');
31880         this._resetLayout();
31881         //this._manageStamps();
31882       
31883         // don't animate first layout
31884         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31885         this.layoutItems( isInstant );
31886       
31887         // flag for initalized
31888         this._isLayoutInited = true;
31889     },
31890     
31891     layoutItems : function( isInstant )
31892     {
31893         //var items = this._getItemsForLayout( this.items );
31894         // original code supports filtering layout items.. we just ignore it..
31895         
31896         this._layoutItems( this.bricks , isInstant );
31897       
31898         this._postLayout();
31899     },
31900     _layoutItems : function ( items , isInstant)
31901     {
31902        //this.fireEvent( 'layout', this, items );
31903     
31904
31905         if ( !items || !items.elements.length ) {
31906           // no items, emit event with empty array
31907             return;
31908         }
31909
31910         var queue = [];
31911         items.each(function(item) {
31912             Roo.log("layout item");
31913             Roo.log(item);
31914             // get x/y object from method
31915             var position = this._getItemLayoutPosition( item );
31916             // enqueue
31917             position.item = item;
31918             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31919             queue.push( position );
31920         }, this);
31921       
31922         this._processLayoutQueue( queue );
31923     },
31924     /** Sets position of item in DOM
31925     * @param {Element} item
31926     * @param {Number} x - horizontal position
31927     * @param {Number} y - vertical position
31928     * @param {Boolean} isInstant - disables transitions
31929     */
31930     _processLayoutQueue : function( queue )
31931     {
31932         for ( var i=0, len = queue.length; i < len; i++ ) {
31933             var obj = queue[i];
31934             obj.item.position('absolute');
31935             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31936         }
31937     },
31938       
31939     
31940     /**
31941     * Any logic you want to do after each layout,
31942     * i.e. size the container
31943     */
31944     _postLayout : function()
31945     {
31946         this.resizeContainer();
31947     },
31948     
31949     resizeContainer : function()
31950     {
31951         if ( !this.isResizingContainer ) {
31952             return;
31953         }
31954         var size = this._getContainerSize();
31955         if ( size ) {
31956             this.el.setSize(size.width,size.height);
31957             this.boxesEl.setSize(size.width,size.height);
31958         }
31959     },
31960     
31961     
31962     
31963     _resetLayout : function()
31964     {
31965         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31966         this.colWidth = this.el.getWidth();
31967         //this.gutter = this.el.getWidth(); 
31968         
31969         this.measureColumns();
31970
31971         // reset column Y
31972         var i = this.cols;
31973         this.colYs = [];
31974         while (i--) {
31975             this.colYs.push( 0 );
31976         }
31977     
31978         this.maxY = 0;
31979     },
31980
31981     measureColumns : function()
31982     {
31983         this.getContainerWidth();
31984       // if columnWidth is 0, default to outerWidth of first item
31985         if ( !this.columnWidth ) {
31986             var firstItem = this.bricks.first();
31987             Roo.log(firstItem);
31988             this.columnWidth  = this.containerWidth;
31989             if (firstItem && firstItem.attr('originalwidth') ) {
31990                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31991             }
31992             // columnWidth fall back to item of first element
31993             Roo.log("set column width?");
31994                         this.initialColumnWidth = this.columnWidth  ;
31995
31996             // if first elem has no width, default to size of container
31997             
31998         }
31999         
32000         
32001         if (this.initialColumnWidth) {
32002             this.columnWidth = this.initialColumnWidth;
32003         }
32004         
32005         
32006             
32007         // column width is fixed at the top - however if container width get's smaller we should
32008         // reduce it...
32009         
32010         // this bit calcs how man columns..
32011             
32012         var columnWidth = this.columnWidth += this.gutter;
32013       
32014         // calculate columns
32015         var containerWidth = this.containerWidth + this.gutter;
32016         
32017         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32018         // fix rounding errors, typically with gutters
32019         var excess = columnWidth - containerWidth % columnWidth;
32020         
32021         
32022         // if overshoot is less than a pixel, round up, otherwise floor it
32023         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32024         cols = Math[ mathMethod ]( cols );
32025         this.cols = Math.max( cols, 1 );
32026         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32027         
32028          // padding positioning..
32029         var totalColWidth = this.cols * this.columnWidth;
32030         var padavail = this.containerWidth - totalColWidth;
32031         // so for 2 columns - we need 3 'pads'
32032         
32033         var padNeeded = (1+this.cols) * this.padWidth;
32034         
32035         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32036         
32037         this.columnWidth += padExtra
32038         //this.padWidth = Math.floor(padavail /  ( this.cols));
32039         
32040         // adjust colum width so that padding is fixed??
32041         
32042         // we have 3 columns ... total = width * 3
32043         // we have X left over... that should be used by 
32044         
32045         //if (this.expandC) {
32046             
32047         //}
32048         
32049         
32050         
32051     },
32052     
32053     getContainerWidth : function()
32054     {
32055        /* // container is parent if fit width
32056         var container = this.isFitWidth ? this.element.parentNode : this.element;
32057         // check that this.size and size are there
32058         // IE8 triggers resize on body size change, so they might not be
32059         
32060         var size = getSize( container );  //FIXME
32061         this.containerWidth = size && size.innerWidth; //FIXME
32062         */
32063          
32064         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32065         
32066     },
32067     
32068     _getItemLayoutPosition : function( item )  // what is item?
32069     {
32070         // we resize the item to our columnWidth..
32071       
32072         item.setWidth(this.columnWidth);
32073         item.autoBoxAdjust  = false;
32074         
32075         var sz = item.getSize();
32076  
32077         // how many columns does this brick span
32078         var remainder = this.containerWidth % this.columnWidth;
32079         
32080         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32081         // round if off by 1 pixel, otherwise use ceil
32082         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32083         colSpan = Math.min( colSpan, this.cols );
32084         
32085         // normally this should be '1' as we dont' currently allow multi width columns..
32086         
32087         var colGroup = this._getColGroup( colSpan );
32088         // get the minimum Y value from the columns
32089         var minimumY = Math.min.apply( Math, colGroup );
32090         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32091         
32092         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32093          
32094         // position the brick
32095         var position = {
32096             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32097             y: this.currentSize.y + minimumY + this.padHeight
32098         };
32099         
32100         Roo.log(position);
32101         // apply setHeight to necessary columns
32102         var setHeight = minimumY + sz.height + this.padHeight;
32103         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32104         
32105         var setSpan = this.cols + 1 - colGroup.length;
32106         for ( var i = 0; i < setSpan; i++ ) {
32107           this.colYs[ shortColIndex + i ] = setHeight ;
32108         }
32109       
32110         return position;
32111     },
32112     
32113     /**
32114      * @param {Number} colSpan - number of columns the element spans
32115      * @returns {Array} colGroup
32116      */
32117     _getColGroup : function( colSpan )
32118     {
32119         if ( colSpan < 2 ) {
32120           // if brick spans only one column, use all the column Ys
32121           return this.colYs;
32122         }
32123       
32124         var colGroup = [];
32125         // how many different places could this brick fit horizontally
32126         var groupCount = this.cols + 1 - colSpan;
32127         // for each group potential horizontal position
32128         for ( var i = 0; i < groupCount; i++ ) {
32129           // make an array of colY values for that one group
32130           var groupColYs = this.colYs.slice( i, i + colSpan );
32131           // and get the max value of the array
32132           colGroup[i] = Math.max.apply( Math, groupColYs );
32133         }
32134         return colGroup;
32135     },
32136     /*
32137     _manageStamp : function( stamp )
32138     {
32139         var stampSize =  stamp.getSize();
32140         var offset = stamp.getBox();
32141         // get the columns that this stamp affects
32142         var firstX = this.isOriginLeft ? offset.x : offset.right;
32143         var lastX = firstX + stampSize.width;
32144         var firstCol = Math.floor( firstX / this.columnWidth );
32145         firstCol = Math.max( 0, firstCol );
32146         
32147         var lastCol = Math.floor( lastX / this.columnWidth );
32148         // lastCol should not go over if multiple of columnWidth #425
32149         lastCol -= lastX % this.columnWidth ? 0 : 1;
32150         lastCol = Math.min( this.cols - 1, lastCol );
32151         
32152         // set colYs to bottom of the stamp
32153         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32154             stampSize.height;
32155             
32156         for ( var i = firstCol; i <= lastCol; i++ ) {
32157           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32158         }
32159     },
32160     */
32161     
32162     _getContainerSize : function()
32163     {
32164         this.maxY = Math.max.apply( Math, this.colYs );
32165         var size = {
32166             height: this.maxY
32167         };
32168       
32169         if ( this.isFitWidth ) {
32170             size.width = this._getContainerFitWidth();
32171         }
32172       
32173         return size;
32174     },
32175     
32176     _getContainerFitWidth : function()
32177     {
32178         var unusedCols = 0;
32179         // count unused columns
32180         var i = this.cols;
32181         while ( --i ) {
32182           if ( this.colYs[i] !== 0 ) {
32183             break;
32184           }
32185           unusedCols++;
32186         }
32187         // fit container to columns that have been used
32188         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32189     },
32190     
32191     needsResizeLayout : function()
32192     {
32193         var previousWidth = this.containerWidth;
32194         this.getContainerWidth();
32195         return previousWidth !== this.containerWidth;
32196     }
32197  
32198 });
32199
32200  
32201
32202  /*
32203  * - LGPL
32204  *
32205  * element
32206  * 
32207  */
32208
32209 /**
32210  * @class Roo.bootstrap.MasonryBrick
32211  * @extends Roo.bootstrap.Component
32212  * Bootstrap MasonryBrick class
32213  * 
32214  * @constructor
32215  * Create a new MasonryBrick
32216  * @param {Object} config The config object
32217  */
32218
32219 Roo.bootstrap.MasonryBrick = function(config){
32220     
32221     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32222     
32223     Roo.bootstrap.MasonryBrick.register(this);
32224     
32225     this.addEvents({
32226         // raw events
32227         /**
32228          * @event click
32229          * When a MasonryBrick is clcik
32230          * @param {Roo.bootstrap.MasonryBrick} this
32231          * @param {Roo.EventObject} e
32232          */
32233         "click" : true
32234     });
32235 };
32236
32237 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32238     
32239     /**
32240      * @cfg {String} title
32241      */   
32242     title : '',
32243     /**
32244      * @cfg {String} html
32245      */   
32246     html : '',
32247     /**
32248      * @cfg {String} bgimage
32249      */   
32250     bgimage : '',
32251     /**
32252      * @cfg {String} videourl
32253      */   
32254     videourl : '',
32255     /**
32256      * @cfg {String} cls
32257      */   
32258     cls : '',
32259     /**
32260      * @cfg {String} href
32261      */   
32262     href : '',
32263     /**
32264      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32265      */   
32266     size : 'xs',
32267     
32268     /**
32269      * @cfg {String} placetitle (center|bottom)
32270      */   
32271     placetitle : '',
32272     
32273     /**
32274      * @cfg {Boolean} isFitContainer defalut true
32275      */   
32276     isFitContainer : true, 
32277     
32278     /**
32279      * @cfg {Boolean} preventDefault defalut false
32280      */   
32281     preventDefault : false, 
32282     
32283     /**
32284      * @cfg {Boolean} inverse defalut false
32285      */   
32286     maskInverse : false, 
32287     
32288     getAutoCreate : function()
32289     {
32290         if(!this.isFitContainer){
32291             return this.getSplitAutoCreate();
32292         }
32293         
32294         var cls = 'masonry-brick masonry-brick-full';
32295         
32296         if(this.href.length){
32297             cls += ' masonry-brick-link';
32298         }
32299         
32300         if(this.bgimage.length){
32301             cls += ' masonry-brick-image';
32302         }
32303         
32304         if(this.maskInverse){
32305             cls += ' mask-inverse';
32306         }
32307         
32308         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32309             cls += ' enable-mask';
32310         }
32311         
32312         if(this.size){
32313             cls += ' masonry-' + this.size + '-brick';
32314         }
32315         
32316         if(this.placetitle.length){
32317             
32318             switch (this.placetitle) {
32319                 case 'center' :
32320                     cls += ' masonry-center-title';
32321                     break;
32322                 case 'bottom' :
32323                     cls += ' masonry-bottom-title';
32324                     break;
32325                 default:
32326                     break;
32327             }
32328             
32329         } else {
32330             if(!this.html.length && !this.bgimage.length){
32331                 cls += ' masonry-center-title';
32332             }
32333
32334             if(!this.html.length && this.bgimage.length){
32335                 cls += ' masonry-bottom-title';
32336             }
32337         }
32338         
32339         if(this.cls){
32340             cls += ' ' + this.cls;
32341         }
32342         
32343         var cfg = {
32344             tag: (this.href.length) ? 'a' : 'div',
32345             cls: cls,
32346             cn: [
32347                 {
32348                     tag: 'div',
32349                     cls: 'masonry-brick-mask'
32350                 },
32351                 {
32352                     tag: 'div',
32353                     cls: 'masonry-brick-paragraph',
32354                     cn: []
32355                 }
32356             ]
32357         };
32358         
32359         if(this.href.length){
32360             cfg.href = this.href;
32361         }
32362         
32363         var cn = cfg.cn[1].cn;
32364         
32365         if(this.title.length){
32366             cn.push({
32367                 tag: 'h4',
32368                 cls: 'masonry-brick-title',
32369                 html: this.title
32370             });
32371         }
32372         
32373         if(this.html.length){
32374             cn.push({
32375                 tag: 'p',
32376                 cls: 'masonry-brick-text',
32377                 html: this.html
32378             });
32379         }
32380         
32381         if (!this.title.length && !this.html.length) {
32382             cfg.cn[1].cls += ' hide';
32383         }
32384         
32385         if(this.bgimage.length){
32386             cfg.cn.push({
32387                 tag: 'img',
32388                 cls: 'masonry-brick-image-view',
32389                 src: this.bgimage
32390             });
32391         }
32392         
32393         if(this.videourl.length){
32394             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32395             // youtube support only?
32396             cfg.cn.push({
32397                 tag: 'iframe',
32398                 cls: 'masonry-brick-image-view',
32399                 src: vurl,
32400                 frameborder : 0,
32401                 allowfullscreen : true
32402             });
32403         }
32404         
32405         return cfg;
32406         
32407     },
32408     
32409     getSplitAutoCreate : function()
32410     {
32411         var cls = 'masonry-brick masonry-brick-split';
32412         
32413         if(this.href.length){
32414             cls += ' masonry-brick-link';
32415         }
32416         
32417         if(this.bgimage.length){
32418             cls += ' masonry-brick-image';
32419         }
32420         
32421         if(this.size){
32422             cls += ' masonry-' + this.size + '-brick';
32423         }
32424         
32425         switch (this.placetitle) {
32426             case 'center' :
32427                 cls += ' masonry-center-title';
32428                 break;
32429             case 'bottom' :
32430                 cls += ' masonry-bottom-title';
32431                 break;
32432             default:
32433                 if(!this.bgimage.length){
32434                     cls += ' masonry-center-title';
32435                 }
32436
32437                 if(this.bgimage.length){
32438                     cls += ' masonry-bottom-title';
32439                 }
32440                 break;
32441         }
32442         
32443         if(this.cls){
32444             cls += ' ' + this.cls;
32445         }
32446         
32447         var cfg = {
32448             tag: (this.href.length) ? 'a' : 'div',
32449             cls: cls,
32450             cn: [
32451                 {
32452                     tag: 'div',
32453                     cls: 'masonry-brick-split-head',
32454                     cn: [
32455                         {
32456                             tag: 'div',
32457                             cls: 'masonry-brick-paragraph',
32458                             cn: []
32459                         }
32460                     ]
32461                 },
32462                 {
32463                     tag: 'div',
32464                     cls: 'masonry-brick-split-body',
32465                     cn: []
32466                 }
32467             ]
32468         };
32469         
32470         if(this.href.length){
32471             cfg.href = this.href;
32472         }
32473         
32474         if(this.title.length){
32475             cfg.cn[0].cn[0].cn.push({
32476                 tag: 'h4',
32477                 cls: 'masonry-brick-title',
32478                 html: this.title
32479             });
32480         }
32481         
32482         if(this.html.length){
32483             cfg.cn[1].cn.push({
32484                 tag: 'p',
32485                 cls: 'masonry-brick-text',
32486                 html: this.html
32487             });
32488         }
32489
32490         if(this.bgimage.length){
32491             cfg.cn[0].cn.push({
32492                 tag: 'img',
32493                 cls: 'masonry-brick-image-view',
32494                 src: this.bgimage
32495             });
32496         }
32497         
32498         if(this.videourl.length){
32499             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32500             // youtube support only?
32501             cfg.cn[0].cn.cn.push({
32502                 tag: 'iframe',
32503                 cls: 'masonry-brick-image-view',
32504                 src: vurl,
32505                 frameborder : 0,
32506                 allowfullscreen : true
32507             });
32508         }
32509         
32510         return cfg;
32511     },
32512     
32513     initEvents: function() 
32514     {
32515         switch (this.size) {
32516             case 'xs' :
32517                 this.x = 1;
32518                 this.y = 1;
32519                 break;
32520             case 'sm' :
32521                 this.x = 2;
32522                 this.y = 2;
32523                 break;
32524             case 'md' :
32525             case 'md-left' :
32526             case 'md-right' :
32527                 this.x = 3;
32528                 this.y = 3;
32529                 break;
32530             case 'tall' :
32531                 this.x = 2;
32532                 this.y = 3;
32533                 break;
32534             case 'wide' :
32535                 this.x = 3;
32536                 this.y = 2;
32537                 break;
32538             case 'wide-thin' :
32539                 this.x = 3;
32540                 this.y = 1;
32541                 break;
32542                         
32543             default :
32544                 break;
32545         }
32546         
32547         if(Roo.isTouch){
32548             this.el.on('touchstart', this.onTouchStart, this);
32549             this.el.on('touchmove', this.onTouchMove, this);
32550             this.el.on('touchend', this.onTouchEnd, this);
32551             this.el.on('contextmenu', this.onContextMenu, this);
32552         } else {
32553             this.el.on('mouseenter'  ,this.enter, this);
32554             this.el.on('mouseleave', this.leave, this);
32555             this.el.on('click', this.onClick, this);
32556         }
32557         
32558         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32559             this.parent().bricks.push(this);   
32560         }
32561         
32562     },
32563     
32564     onClick: function(e, el)
32565     {
32566         var time = this.endTimer - this.startTimer;
32567         // Roo.log(e.preventDefault());
32568         if(Roo.isTouch){
32569             if(time > 1000){
32570                 e.preventDefault();
32571                 return;
32572             }
32573         }
32574         
32575         if(!this.preventDefault){
32576             return;
32577         }
32578         
32579         e.preventDefault();
32580         
32581         if (this.activcClass != '') {
32582             this.selectBrick();
32583         }
32584         
32585         this.fireEvent('click', this);
32586     },
32587     
32588     enter: function(e, el)
32589     {
32590         e.preventDefault();
32591         
32592         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32593             return;
32594         }
32595         
32596         if(this.bgimage.length && this.html.length){
32597             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32598         }
32599     },
32600     
32601     leave: function(e, el)
32602     {
32603         e.preventDefault();
32604         
32605         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32606             return;
32607         }
32608         
32609         if(this.bgimage.length && this.html.length){
32610             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32611         }
32612     },
32613     
32614     onTouchStart: function(e, el)
32615     {
32616 //        e.preventDefault();
32617         
32618         this.touchmoved = false;
32619         
32620         if(!this.isFitContainer){
32621             return;
32622         }
32623         
32624         if(!this.bgimage.length || !this.html.length){
32625             return;
32626         }
32627         
32628         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32629         
32630         this.timer = new Date().getTime();
32631         
32632     },
32633     
32634     onTouchMove: function(e, el)
32635     {
32636         this.touchmoved = true;
32637     },
32638     
32639     onContextMenu : function(e,el)
32640     {
32641         e.preventDefault();
32642         e.stopPropagation();
32643         return false;
32644     },
32645     
32646     onTouchEnd: function(e, el)
32647     {
32648 //        e.preventDefault();
32649         
32650         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32651         
32652             this.leave(e,el);
32653             
32654             return;
32655         }
32656         
32657         if(!this.bgimage.length || !this.html.length){
32658             
32659             if(this.href.length){
32660                 window.location.href = this.href;
32661             }
32662             
32663             return;
32664         }
32665         
32666         if(!this.isFitContainer){
32667             return;
32668         }
32669         
32670         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32671         
32672         window.location.href = this.href;
32673     },
32674     
32675     //selection on single brick only
32676     selectBrick : function() {
32677         
32678         if (!this.parentId) {
32679             return;
32680         }
32681         
32682         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32683         var index = m.selectedBrick.indexOf(this.id);
32684         
32685         if ( index > -1) {
32686             m.selectedBrick.splice(index,1);
32687             this.el.removeClass(this.activeClass);
32688             return;
32689         }
32690         
32691         for(var i = 0; i < m.selectedBrick.length; i++) {
32692             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32693             b.el.removeClass(b.activeClass);
32694         }
32695         
32696         m.selectedBrick = [];
32697         
32698         m.selectedBrick.push(this.id);
32699         this.el.addClass(this.activeClass);
32700         return;
32701     }
32702     
32703 });
32704
32705 Roo.apply(Roo.bootstrap.MasonryBrick, {
32706     
32707     //groups: {},
32708     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32709      /**
32710     * register a Masonry Brick
32711     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32712     */
32713     
32714     register : function(brick)
32715     {
32716         //this.groups[brick.id] = brick;
32717         this.groups.add(brick.id, brick);
32718     },
32719     /**
32720     * fetch a  masonry brick based on the masonry brick ID
32721     * @param {string} the masonry brick to add
32722     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32723     */
32724     
32725     get: function(brick_id) 
32726     {
32727         // if (typeof(this.groups[brick_id]) == 'undefined') {
32728         //     return false;
32729         // }
32730         // return this.groups[brick_id] ;
32731         
32732         if(this.groups.key(brick_id)) {
32733             return this.groups.key(brick_id);
32734         }
32735         
32736         return false;
32737     }
32738     
32739     
32740     
32741 });
32742
32743  /*
32744  * - LGPL
32745  *
32746  * element
32747  * 
32748  */
32749
32750 /**
32751  * @class Roo.bootstrap.Brick
32752  * @extends Roo.bootstrap.Component
32753  * Bootstrap Brick class
32754  * 
32755  * @constructor
32756  * Create a new Brick
32757  * @param {Object} config The config object
32758  */
32759
32760 Roo.bootstrap.Brick = function(config){
32761     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32762     
32763     this.addEvents({
32764         // raw events
32765         /**
32766          * @event click
32767          * When a Brick is click
32768          * @param {Roo.bootstrap.Brick} this
32769          * @param {Roo.EventObject} e
32770          */
32771         "click" : true
32772     });
32773 };
32774
32775 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32776     
32777     /**
32778      * @cfg {String} title
32779      */   
32780     title : '',
32781     /**
32782      * @cfg {String} html
32783      */   
32784     html : '',
32785     /**
32786      * @cfg {String} bgimage
32787      */   
32788     bgimage : '',
32789     /**
32790      * @cfg {String} cls
32791      */   
32792     cls : '',
32793     /**
32794      * @cfg {String} href
32795      */   
32796     href : '',
32797     /**
32798      * @cfg {String} video
32799      */   
32800     video : '',
32801     /**
32802      * @cfg {Boolean} square
32803      */   
32804     square : true,
32805     
32806     getAutoCreate : function()
32807     {
32808         var cls = 'roo-brick';
32809         
32810         if(this.href.length){
32811             cls += ' roo-brick-link';
32812         }
32813         
32814         if(this.bgimage.length){
32815             cls += ' roo-brick-image';
32816         }
32817         
32818         if(!this.html.length && !this.bgimage.length){
32819             cls += ' roo-brick-center-title';
32820         }
32821         
32822         if(!this.html.length && this.bgimage.length){
32823             cls += ' roo-brick-bottom-title';
32824         }
32825         
32826         if(this.cls){
32827             cls += ' ' + this.cls;
32828         }
32829         
32830         var cfg = {
32831             tag: (this.href.length) ? 'a' : 'div',
32832             cls: cls,
32833             cn: [
32834                 {
32835                     tag: 'div',
32836                     cls: 'roo-brick-paragraph',
32837                     cn: []
32838                 }
32839             ]
32840         };
32841         
32842         if(this.href.length){
32843             cfg.href = this.href;
32844         }
32845         
32846         var cn = cfg.cn[0].cn;
32847         
32848         if(this.title.length){
32849             cn.push({
32850                 tag: 'h4',
32851                 cls: 'roo-brick-title',
32852                 html: this.title
32853             });
32854         }
32855         
32856         if(this.html.length){
32857             cn.push({
32858                 tag: 'p',
32859                 cls: 'roo-brick-text',
32860                 html: this.html
32861             });
32862         } else {
32863             cn.cls += ' hide';
32864         }
32865         
32866         if(this.bgimage.length){
32867             cfg.cn.push({
32868                 tag: 'img',
32869                 cls: 'roo-brick-image-view',
32870                 src: this.bgimage
32871             });
32872         }
32873         
32874         return cfg;
32875     },
32876     
32877     initEvents: function() 
32878     {
32879         if(this.title.length || this.html.length){
32880             this.el.on('mouseenter'  ,this.enter, this);
32881             this.el.on('mouseleave', this.leave, this);
32882         }
32883         
32884         Roo.EventManager.onWindowResize(this.resize, this); 
32885         
32886         if(this.bgimage.length){
32887             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32888             this.imageEl.on('load', this.onImageLoad, this);
32889             return;
32890         }
32891         
32892         this.resize();
32893     },
32894     
32895     onImageLoad : function()
32896     {
32897         this.resize();
32898     },
32899     
32900     resize : function()
32901     {
32902         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32903         
32904         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32905         
32906         if(this.bgimage.length){
32907             var image = this.el.select('.roo-brick-image-view', true).first();
32908             
32909             image.setWidth(paragraph.getWidth());
32910             
32911             if(this.square){
32912                 image.setHeight(paragraph.getWidth());
32913             }
32914             
32915             this.el.setHeight(image.getHeight());
32916             paragraph.setHeight(image.getHeight());
32917             
32918         }
32919         
32920     },
32921     
32922     enter: function(e, el)
32923     {
32924         e.preventDefault();
32925         
32926         if(this.bgimage.length){
32927             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32928             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32929         }
32930     },
32931     
32932     leave: function(e, el)
32933     {
32934         e.preventDefault();
32935         
32936         if(this.bgimage.length){
32937             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32938             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32939         }
32940     }
32941     
32942 });
32943
32944  
32945
32946  /*
32947  * - LGPL
32948  *
32949  * Input
32950  * 
32951  */
32952
32953 /**
32954  * @class Roo.bootstrap.NumberField
32955  * @extends Roo.bootstrap.Input
32956  * Bootstrap NumberField class
32957  * 
32958  * 
32959  * 
32960  * 
32961  * @constructor
32962  * Create a new NumberField
32963  * @param {Object} config The config object
32964  */
32965
32966 Roo.bootstrap.NumberField = function(config){
32967     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32968 };
32969
32970 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32971     
32972     /**
32973      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32974      */
32975     allowDecimals : true,
32976     /**
32977      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32978      */
32979     decimalSeparator : ".",
32980     /**
32981      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32982      */
32983     decimalPrecision : 2,
32984     /**
32985      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32986      */
32987     allowNegative : true,
32988     /**
32989      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32990      */
32991     minValue : Number.NEGATIVE_INFINITY,
32992     /**
32993      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32994      */
32995     maxValue : Number.MAX_VALUE,
32996     /**
32997      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32998      */
32999     minText : "The minimum value for this field is {0}",
33000     /**
33001      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33002      */
33003     maxText : "The maximum value for this field is {0}",
33004     /**
33005      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33006      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33007      */
33008     nanText : "{0} is not a valid number",
33009     /**
33010      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33011      */
33012     castInt : true,
33013
33014     // private
33015     initEvents : function()
33016     {   
33017         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33018         
33019         var allowed = "0123456789";
33020         
33021         if(this.allowDecimals){
33022             allowed += this.decimalSeparator;
33023         }
33024         
33025         if(this.allowNegative){
33026             allowed += "-";
33027         }
33028         
33029         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33030         
33031         var keyPress = function(e){
33032             
33033             var k = e.getKey();
33034             
33035             var c = e.getCharCode();
33036             
33037             if(
33038                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33039                     allowed.indexOf(String.fromCharCode(c)) === -1
33040             ){
33041                 e.stopEvent();
33042                 return;
33043             }
33044             
33045             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33046                 return;
33047             }
33048             
33049             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33050                 e.stopEvent();
33051             }
33052         };
33053         
33054         this.el.on("keypress", keyPress, this);
33055     },
33056     
33057     validateValue : function(value)
33058     {
33059         
33060         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33061             return false;
33062         }
33063         
33064         var num = this.parseValue(value);
33065         
33066         if(isNaN(num)){
33067             this.markInvalid(String.format(this.nanText, value));
33068             return false;
33069         }
33070         
33071         if(num < this.minValue){
33072             this.markInvalid(String.format(this.minText, this.minValue));
33073             return false;
33074         }
33075         
33076         if(num > this.maxValue){
33077             this.markInvalid(String.format(this.maxText, this.maxValue));
33078             return false;
33079         }
33080         
33081         return true;
33082     },
33083
33084     getValue : function()
33085     {
33086         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33087     },
33088
33089     parseValue : function(value)
33090     {
33091         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33092         return isNaN(value) ? '' : value;
33093     },
33094
33095     fixPrecision : function(value)
33096     {
33097         var nan = isNaN(value);
33098         
33099         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33100             return nan ? '' : value;
33101         }
33102         return parseFloat(value).toFixed(this.decimalPrecision);
33103     },
33104
33105     setValue : function(v)
33106     {
33107         v = this.fixPrecision(v);
33108         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33109     },
33110
33111     decimalPrecisionFcn : function(v)
33112     {
33113         return Math.floor(v);
33114     },
33115
33116     beforeBlur : function()
33117     {
33118         if(!this.castInt){
33119             return;
33120         }
33121         
33122         var v = this.parseValue(this.getRawValue());
33123         if(v){
33124             this.setValue(v);
33125         }
33126     }
33127     
33128 });
33129
33130  
33131
33132 /*
33133 * Licence: LGPL
33134 */
33135
33136 /**
33137  * @class Roo.bootstrap.DocumentSlider
33138  * @extends Roo.bootstrap.Component
33139  * Bootstrap DocumentSlider class
33140  * 
33141  * @constructor
33142  * Create a new DocumentViewer
33143  * @param {Object} config The config object
33144  */
33145
33146 Roo.bootstrap.DocumentSlider = function(config){
33147     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33148     
33149     this.files = [];
33150     
33151     this.addEvents({
33152         /**
33153          * @event initial
33154          * Fire after initEvent
33155          * @param {Roo.bootstrap.DocumentSlider} this
33156          */
33157         "initial" : true,
33158         /**
33159          * @event update
33160          * Fire after update
33161          * @param {Roo.bootstrap.DocumentSlider} this
33162          */
33163         "update" : true,
33164         /**
33165          * @event click
33166          * Fire after click
33167          * @param {Roo.bootstrap.DocumentSlider} this
33168          */
33169         "click" : true
33170     });
33171 };
33172
33173 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33174     
33175     files : false,
33176     
33177     indicator : 0,
33178     
33179     getAutoCreate : function()
33180     {
33181         var cfg = {
33182             tag : 'div',
33183             cls : 'roo-document-slider',
33184             cn : [
33185                 {
33186                     tag : 'div',
33187                     cls : 'roo-document-slider-header',
33188                     cn : [
33189                         {
33190                             tag : 'div',
33191                             cls : 'roo-document-slider-header-title'
33192                         }
33193                     ]
33194                 },
33195                 {
33196                     tag : 'div',
33197                     cls : 'roo-document-slider-body',
33198                     cn : [
33199                         {
33200                             tag : 'div',
33201                             cls : 'roo-document-slider-prev',
33202                             cn : [
33203                                 {
33204                                     tag : 'i',
33205                                     cls : 'fa fa-chevron-left'
33206                                 }
33207                             ]
33208                         },
33209                         {
33210                             tag : 'div',
33211                             cls : 'roo-document-slider-thumb',
33212                             cn : [
33213                                 {
33214                                     tag : 'img',
33215                                     cls : 'roo-document-slider-image'
33216                                 }
33217                             ]
33218                         },
33219                         {
33220                             tag : 'div',
33221                             cls : 'roo-document-slider-next',
33222                             cn : [
33223                                 {
33224                                     tag : 'i',
33225                                     cls : 'fa fa-chevron-right'
33226                                 }
33227                             ]
33228                         }
33229                     ]
33230                 }
33231             ]
33232         };
33233         
33234         return cfg;
33235     },
33236     
33237     initEvents : function()
33238     {
33239         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33240         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33241         
33242         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33243         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33244         
33245         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33246         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33247         
33248         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33249         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33250         
33251         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33252         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33253         
33254         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33255         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33256         
33257         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33258         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33259         
33260         this.thumbEl.on('click', this.onClick, this);
33261         
33262         this.prevIndicator.on('click', this.prev, this);
33263         
33264         this.nextIndicator.on('click', this.next, this);
33265         
33266     },
33267     
33268     initial : function()
33269     {
33270         if(this.files.length){
33271             this.indicator = 1;
33272             this.update()
33273         }
33274         
33275         this.fireEvent('initial', this);
33276     },
33277     
33278     update : function()
33279     {
33280         this.imageEl.attr('src', this.files[this.indicator - 1]);
33281         
33282         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33283         
33284         this.prevIndicator.show();
33285         
33286         if(this.indicator == 1){
33287             this.prevIndicator.hide();
33288         }
33289         
33290         this.nextIndicator.show();
33291         
33292         if(this.indicator == this.files.length){
33293             this.nextIndicator.hide();
33294         }
33295         
33296         this.thumbEl.scrollTo('top');
33297         
33298         this.fireEvent('update', this);
33299     },
33300     
33301     onClick : function(e)
33302     {
33303         e.preventDefault();
33304         
33305         this.fireEvent('click', this);
33306     },
33307     
33308     prev : function(e)
33309     {
33310         e.preventDefault();
33311         
33312         this.indicator = Math.max(1, this.indicator - 1);
33313         
33314         this.update();
33315     },
33316     
33317     next : function(e)
33318     {
33319         e.preventDefault();
33320         
33321         this.indicator = Math.min(this.files.length, this.indicator + 1);
33322         
33323         this.update();
33324     }
33325 });
33326 /*
33327  * - LGPL
33328  *
33329  * RadioSet
33330  *
33331  *
33332  */
33333
33334 /**
33335  * @class Roo.bootstrap.RadioSet
33336  * @extends Roo.bootstrap.Input
33337  * Bootstrap RadioSet class
33338  * @cfg {String} indicatorpos (left|right) default left
33339  * @cfg {Boolean} inline (true|false) inline the element (default true)
33340  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33341  * @constructor
33342  * Create a new RadioSet
33343  * @param {Object} config The config object
33344  */
33345
33346 Roo.bootstrap.RadioSet = function(config){
33347     
33348     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33349     
33350     this.radioes = [];
33351     
33352     Roo.bootstrap.RadioSet.register(this);
33353     
33354     this.addEvents({
33355         /**
33356         * @event check
33357         * Fires when the element is checked or unchecked.
33358         * @param {Roo.bootstrap.RadioSet} this This radio
33359         * @param {Roo.bootstrap.Radio} item The checked item
33360         */
33361        check : true
33362     });
33363     
33364 };
33365
33366 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33367
33368     radioes : false,
33369     
33370     inline : true,
33371     
33372     weight : '',
33373     
33374     indicatorpos : 'left',
33375     
33376     getAutoCreate : function()
33377     {
33378         var label = {
33379             tag : 'label',
33380             cls : 'roo-radio-set-label',
33381             cn : [
33382                 {
33383                     tag : 'span',
33384                     html : this.fieldLabel
33385                 }
33386             ]
33387         };
33388         
33389         if(this.indicatorpos == 'left'){
33390             label.cn.unshift({
33391                 tag : 'i',
33392                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33393                 tooltip : 'This field is required'
33394             });
33395         } else {
33396             label.cn.push({
33397                 tag : 'i',
33398                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33399                 tooltip : 'This field is required'
33400             });
33401         }
33402         
33403         var items = {
33404             tag : 'div',
33405             cls : 'roo-radio-set-items'
33406         };
33407         
33408         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33409         
33410         if (align === 'left' && this.fieldLabel.length) {
33411             
33412             items = {
33413                 cls : "roo-radio-set-right", 
33414                 cn: [
33415                     items
33416                 ]
33417             };
33418             
33419             if(this.labelWidth > 12){
33420                 label.style = "width: " + this.labelWidth + 'px';
33421             }
33422             
33423             if(this.labelWidth < 13 && this.labelmd == 0){
33424                 this.labelmd = this.labelWidth;
33425             }
33426             
33427             if(this.labellg > 0){
33428                 label.cls += ' col-lg-' + this.labellg;
33429                 items.cls += ' col-lg-' + (12 - this.labellg);
33430             }
33431             
33432             if(this.labelmd > 0){
33433                 label.cls += ' col-md-' + this.labelmd;
33434                 items.cls += ' col-md-' + (12 - this.labelmd);
33435             }
33436             
33437             if(this.labelsm > 0){
33438                 label.cls += ' col-sm-' + this.labelsm;
33439                 items.cls += ' col-sm-' + (12 - this.labelsm);
33440             }
33441             
33442             if(this.labelxs > 0){
33443                 label.cls += ' col-xs-' + this.labelxs;
33444                 items.cls += ' col-xs-' + (12 - this.labelxs);
33445             }
33446         }
33447         
33448         var cfg = {
33449             tag : 'div',
33450             cls : 'roo-radio-set',
33451             cn : [
33452                 {
33453                     tag : 'input',
33454                     cls : 'roo-radio-set-input',
33455                     type : 'hidden',
33456                     name : this.name,
33457                     value : this.value ? this.value :  ''
33458                 },
33459                 label,
33460                 items
33461             ]
33462         };
33463         
33464         if(this.weight.length){
33465             cfg.cls += ' roo-radio-' + this.weight;
33466         }
33467         
33468         if(this.inline) {
33469             cfg.cls += ' roo-radio-set-inline';
33470         }
33471         
33472         var settings=this;
33473         ['xs','sm','md','lg'].map(function(size){
33474             if (settings[size]) {
33475                 cfg.cls += ' col-' + size + '-' + settings[size];
33476             }
33477         });
33478         
33479         return cfg;
33480         
33481     },
33482
33483     initEvents : function()
33484     {
33485         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33486         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33487         
33488         if(!this.fieldLabel.length){
33489             this.labelEl.hide();
33490         }
33491         
33492         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33493         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33494         
33495         this.indicatorEl().addClass('invisible');
33496         
33497         this.originalValue = this.getValue();
33498         
33499     },
33500     
33501     inputEl: function ()
33502     {
33503         return this.el.select('.roo-radio-set-input', true).first();
33504     },
33505     
33506     getChildContainer : function()
33507     {
33508         return this.itemsEl;
33509     },
33510     
33511     register : function(item)
33512     {
33513         this.radioes.push(item);
33514         
33515     },
33516     
33517     validate : function()
33518     {   
33519         var valid = false;
33520         
33521         Roo.each(this.radioes, function(i){
33522             if(!i.checked){
33523                 return;
33524             }
33525             
33526             valid = true;
33527             return false;
33528         });
33529         
33530         if(this.allowBlank) {
33531             return true;
33532         }
33533         
33534         if(this.disabled || valid){
33535             this.markValid();
33536             return true;
33537         }
33538         
33539         this.markInvalid();
33540         return false;
33541         
33542     },
33543     
33544     markValid : function()
33545     {
33546         if(this.labelEl.isVisible(true)){
33547             this.indicatorEl().removeClass('visible');
33548             this.indicatorEl().addClass('invisible');
33549         }
33550         
33551         this.el.removeClass([this.invalidClass, this.validClass]);
33552         this.el.addClass(this.validClass);
33553         
33554         this.fireEvent('valid', this);
33555     },
33556     
33557     markInvalid : function(msg)
33558     {
33559         if(this.allowBlank || this.disabled){
33560             return;
33561         }
33562         
33563         if(this.labelEl.isVisible(true)){
33564             this.indicatorEl().removeClass('invisible');
33565             this.indicatorEl().addClass('visible');
33566         }
33567         
33568         this.el.removeClass([this.invalidClass, this.validClass]);
33569         this.el.addClass(this.invalidClass);
33570         
33571         this.fireEvent('invalid', this, msg);
33572         
33573     },
33574     
33575     setValue : function(v, suppressEvent)
33576     {   
33577         if(this.value === v){
33578             return;
33579         }
33580         
33581         this.value = v;
33582         
33583         if(this.rendered){
33584             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33585         }
33586         
33587         Roo.each(this.radioes, function(i){
33588             
33589             i.checked = false;
33590             i.el.removeClass('checked');
33591             
33592             if(i.value === v || i.value.toString() === v.toString()){
33593                 i.checked = true;
33594                 i.el.addClass('checked');
33595                 
33596                 if(suppressEvent !== true){
33597                     this.fireEvent('check', this, i);
33598                 }
33599             }
33600             
33601         }, this);
33602         
33603         this.validate();
33604     },
33605     
33606     clearInvalid : function(){
33607         
33608         if(!this.el || this.preventMark){
33609             return;
33610         }
33611         
33612         this.el.removeClass([this.invalidClass]);
33613         
33614         this.fireEvent('valid', this);
33615     }
33616     
33617 });
33618
33619 Roo.apply(Roo.bootstrap.RadioSet, {
33620     
33621     groups: {},
33622     
33623     register : function(set)
33624     {
33625         this.groups[set.name] = set;
33626     },
33627     
33628     get: function(name) 
33629     {
33630         if (typeof(this.groups[name]) == 'undefined') {
33631             return false;
33632         }
33633         
33634         return this.groups[name] ;
33635     }
33636     
33637 });
33638 /*
33639  * Based on:
33640  * Ext JS Library 1.1.1
33641  * Copyright(c) 2006-2007, Ext JS, LLC.
33642  *
33643  * Originally Released Under LGPL - original licence link has changed is not relivant.
33644  *
33645  * Fork - LGPL
33646  * <script type="text/javascript">
33647  */
33648
33649
33650 /**
33651  * @class Roo.bootstrap.SplitBar
33652  * @extends Roo.util.Observable
33653  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33654  * <br><br>
33655  * Usage:
33656  * <pre><code>
33657 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33658                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33659 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33660 split.minSize = 100;
33661 split.maxSize = 600;
33662 split.animate = true;
33663 split.on('moved', splitterMoved);
33664 </code></pre>
33665  * @constructor
33666  * Create a new SplitBar
33667  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33668  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33669  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33670  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33671                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33672                         position of the SplitBar).
33673  */
33674 Roo.bootstrap.SplitBar = function(cfg){
33675     
33676     /** @private */
33677     
33678     //{
33679     //  dragElement : elm
33680     //  resizingElement: el,
33681         // optional..
33682     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33683     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33684         // existingProxy ???
33685     //}
33686     
33687     this.el = Roo.get(cfg.dragElement, true);
33688     this.el.dom.unselectable = "on";
33689     /** @private */
33690     this.resizingEl = Roo.get(cfg.resizingElement, true);
33691
33692     /**
33693      * @private
33694      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33695      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33696      * @type Number
33697      */
33698     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33699     
33700     /**
33701      * The minimum size of the resizing element. (Defaults to 0)
33702      * @type Number
33703      */
33704     this.minSize = 0;
33705     
33706     /**
33707      * The maximum size of the resizing element. (Defaults to 2000)
33708      * @type Number
33709      */
33710     this.maxSize = 2000;
33711     
33712     /**
33713      * Whether to animate the transition to the new size
33714      * @type Boolean
33715      */
33716     this.animate = false;
33717     
33718     /**
33719      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33720      * @type Boolean
33721      */
33722     this.useShim = false;
33723     
33724     /** @private */
33725     this.shim = null;
33726     
33727     if(!cfg.existingProxy){
33728         /** @private */
33729         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33730     }else{
33731         this.proxy = Roo.get(cfg.existingProxy).dom;
33732     }
33733     /** @private */
33734     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33735     
33736     /** @private */
33737     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33738     
33739     /** @private */
33740     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33741     
33742     /** @private */
33743     this.dragSpecs = {};
33744     
33745     /**
33746      * @private The adapter to use to positon and resize elements
33747      */
33748     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33749     this.adapter.init(this);
33750     
33751     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33752         /** @private */
33753         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33754         this.el.addClass("roo-splitbar-h");
33755     }else{
33756         /** @private */
33757         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33758         this.el.addClass("roo-splitbar-v");
33759     }
33760     
33761     this.addEvents({
33762         /**
33763          * @event resize
33764          * Fires when the splitter is moved (alias for {@link #event-moved})
33765          * @param {Roo.bootstrap.SplitBar} this
33766          * @param {Number} newSize the new width or height
33767          */
33768         "resize" : true,
33769         /**
33770          * @event moved
33771          * Fires when the splitter is moved
33772          * @param {Roo.bootstrap.SplitBar} this
33773          * @param {Number} newSize the new width or height
33774          */
33775         "moved" : true,
33776         /**
33777          * @event beforeresize
33778          * Fires before the splitter is dragged
33779          * @param {Roo.bootstrap.SplitBar} this
33780          */
33781         "beforeresize" : true,
33782
33783         "beforeapply" : true
33784     });
33785
33786     Roo.util.Observable.call(this);
33787 };
33788
33789 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33790     onStartProxyDrag : function(x, y){
33791         this.fireEvent("beforeresize", this);
33792         if(!this.overlay){
33793             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33794             o.unselectable();
33795             o.enableDisplayMode("block");
33796             // all splitbars share the same overlay
33797             Roo.bootstrap.SplitBar.prototype.overlay = o;
33798         }
33799         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33800         this.overlay.show();
33801         Roo.get(this.proxy).setDisplayed("block");
33802         var size = this.adapter.getElementSize(this);
33803         this.activeMinSize = this.getMinimumSize();;
33804         this.activeMaxSize = this.getMaximumSize();;
33805         var c1 = size - this.activeMinSize;
33806         var c2 = Math.max(this.activeMaxSize - size, 0);
33807         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33808             this.dd.resetConstraints();
33809             this.dd.setXConstraint(
33810                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33811                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33812             );
33813             this.dd.setYConstraint(0, 0);
33814         }else{
33815             this.dd.resetConstraints();
33816             this.dd.setXConstraint(0, 0);
33817             this.dd.setYConstraint(
33818                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33819                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33820             );
33821          }
33822         this.dragSpecs.startSize = size;
33823         this.dragSpecs.startPoint = [x, y];
33824         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33825     },
33826     
33827     /** 
33828      * @private Called after the drag operation by the DDProxy
33829      */
33830     onEndProxyDrag : function(e){
33831         Roo.get(this.proxy).setDisplayed(false);
33832         var endPoint = Roo.lib.Event.getXY(e);
33833         if(this.overlay){
33834             this.overlay.hide();
33835         }
33836         var newSize;
33837         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33838             newSize = this.dragSpecs.startSize + 
33839                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33840                     endPoint[0] - this.dragSpecs.startPoint[0] :
33841                     this.dragSpecs.startPoint[0] - endPoint[0]
33842                 );
33843         }else{
33844             newSize = this.dragSpecs.startSize + 
33845                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33846                     endPoint[1] - this.dragSpecs.startPoint[1] :
33847                     this.dragSpecs.startPoint[1] - endPoint[1]
33848                 );
33849         }
33850         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33851         if(newSize != this.dragSpecs.startSize){
33852             if(this.fireEvent('beforeapply', this, newSize) !== false){
33853                 this.adapter.setElementSize(this, newSize);
33854                 this.fireEvent("moved", this, newSize);
33855                 this.fireEvent("resize", this, newSize);
33856             }
33857         }
33858     },
33859     
33860     /**
33861      * Get the adapter this SplitBar uses
33862      * @return The adapter object
33863      */
33864     getAdapter : function(){
33865         return this.adapter;
33866     },
33867     
33868     /**
33869      * Set the adapter this SplitBar uses
33870      * @param {Object} adapter A SplitBar adapter object
33871      */
33872     setAdapter : function(adapter){
33873         this.adapter = adapter;
33874         this.adapter.init(this);
33875     },
33876     
33877     /**
33878      * Gets the minimum size for the resizing element
33879      * @return {Number} The minimum size
33880      */
33881     getMinimumSize : function(){
33882         return this.minSize;
33883     },
33884     
33885     /**
33886      * Sets the minimum size for the resizing element
33887      * @param {Number} minSize The minimum size
33888      */
33889     setMinimumSize : function(minSize){
33890         this.minSize = minSize;
33891     },
33892     
33893     /**
33894      * Gets the maximum size for the resizing element
33895      * @return {Number} The maximum size
33896      */
33897     getMaximumSize : function(){
33898         return this.maxSize;
33899     },
33900     
33901     /**
33902      * Sets the maximum size for the resizing element
33903      * @param {Number} maxSize The maximum size
33904      */
33905     setMaximumSize : function(maxSize){
33906         this.maxSize = maxSize;
33907     },
33908     
33909     /**
33910      * Sets the initialize size for the resizing element
33911      * @param {Number} size The initial size
33912      */
33913     setCurrentSize : function(size){
33914         var oldAnimate = this.animate;
33915         this.animate = false;
33916         this.adapter.setElementSize(this, size);
33917         this.animate = oldAnimate;
33918     },
33919     
33920     /**
33921      * Destroy this splitbar. 
33922      * @param {Boolean} removeEl True to remove the element
33923      */
33924     destroy : function(removeEl){
33925         if(this.shim){
33926             this.shim.remove();
33927         }
33928         this.dd.unreg();
33929         this.proxy.parentNode.removeChild(this.proxy);
33930         if(removeEl){
33931             this.el.remove();
33932         }
33933     }
33934 });
33935
33936 /**
33937  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
33938  */
33939 Roo.bootstrap.SplitBar.createProxy = function(dir){
33940     var proxy = new Roo.Element(document.createElement("div"));
33941     proxy.unselectable();
33942     var cls = 'roo-splitbar-proxy';
33943     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33944     document.body.appendChild(proxy.dom);
33945     return proxy.dom;
33946 };
33947
33948 /** 
33949  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33950  * Default Adapter. It assumes the splitter and resizing element are not positioned
33951  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33952  */
33953 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33954 };
33955
33956 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33957     // do nothing for now
33958     init : function(s){
33959     
33960     },
33961     /**
33962      * Called before drag operations to get the current size of the resizing element. 
33963      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33964      */
33965      getElementSize : function(s){
33966         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33967             return s.resizingEl.getWidth();
33968         }else{
33969             return s.resizingEl.getHeight();
33970         }
33971     },
33972     
33973     /**
33974      * Called after drag operations to set the size of the resizing element.
33975      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33976      * @param {Number} newSize The new size to set
33977      * @param {Function} onComplete A function to be invoked when resizing is complete
33978      */
33979     setElementSize : function(s, newSize, onComplete){
33980         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33981             if(!s.animate){
33982                 s.resizingEl.setWidth(newSize);
33983                 if(onComplete){
33984                     onComplete(s, newSize);
33985                 }
33986             }else{
33987                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33988             }
33989         }else{
33990             
33991             if(!s.animate){
33992                 s.resizingEl.setHeight(newSize);
33993                 if(onComplete){
33994                     onComplete(s, newSize);
33995                 }
33996             }else{
33997                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33998             }
33999         }
34000     }
34001 };
34002
34003 /** 
34004  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34005  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34006  * Adapter that  moves the splitter element to align with the resized sizing element. 
34007  * Used with an absolute positioned SplitBar.
34008  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34009  * document.body, make sure you assign an id to the body element.
34010  */
34011 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34012     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34013     this.container = Roo.get(container);
34014 };
34015
34016 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34017     init : function(s){
34018         this.basic.init(s);
34019     },
34020     
34021     getElementSize : function(s){
34022         return this.basic.getElementSize(s);
34023     },
34024     
34025     setElementSize : function(s, newSize, onComplete){
34026         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34027     },
34028     
34029     moveSplitter : function(s){
34030         var yes = Roo.bootstrap.SplitBar;
34031         switch(s.placement){
34032             case yes.LEFT:
34033                 s.el.setX(s.resizingEl.getRight());
34034                 break;
34035             case yes.RIGHT:
34036                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34037                 break;
34038             case yes.TOP:
34039                 s.el.setY(s.resizingEl.getBottom());
34040                 break;
34041             case yes.BOTTOM:
34042                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34043                 break;
34044         }
34045     }
34046 };
34047
34048 /**
34049  * Orientation constant - Create a vertical SplitBar
34050  * @static
34051  * @type Number
34052  */
34053 Roo.bootstrap.SplitBar.VERTICAL = 1;
34054
34055 /**
34056  * Orientation constant - Create a horizontal SplitBar
34057  * @static
34058  * @type Number
34059  */
34060 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34061
34062 /**
34063  * Placement constant - The resizing element is to the left of the splitter element
34064  * @static
34065  * @type Number
34066  */
34067 Roo.bootstrap.SplitBar.LEFT = 1;
34068
34069 /**
34070  * Placement constant - The resizing element is to the right of the splitter element
34071  * @static
34072  * @type Number
34073  */
34074 Roo.bootstrap.SplitBar.RIGHT = 2;
34075
34076 /**
34077  * Placement constant - The resizing element is positioned above the splitter element
34078  * @static
34079  * @type Number
34080  */
34081 Roo.bootstrap.SplitBar.TOP = 3;
34082
34083 /**
34084  * Placement constant - The resizing element is positioned under splitter element
34085  * @static
34086  * @type Number
34087  */
34088 Roo.bootstrap.SplitBar.BOTTOM = 4;
34089 Roo.namespace("Roo.bootstrap.layout");/*
34090  * Based on:
34091  * Ext JS Library 1.1.1
34092  * Copyright(c) 2006-2007, Ext JS, LLC.
34093  *
34094  * Originally Released Under LGPL - original licence link has changed is not relivant.
34095  *
34096  * Fork - LGPL
34097  * <script type="text/javascript">
34098  */
34099
34100 /**
34101  * @class Roo.bootstrap.layout.Manager
34102  * @extends Roo.bootstrap.Component
34103  * Base class for layout managers.
34104  */
34105 Roo.bootstrap.layout.Manager = function(config)
34106 {
34107     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34108
34109
34110
34111
34112
34113     /** false to disable window resize monitoring @type Boolean */
34114     this.monitorWindowResize = true;
34115     this.regions = {};
34116     this.addEvents({
34117         /**
34118          * @event layout
34119          * Fires when a layout is performed.
34120          * @param {Roo.LayoutManager} this
34121          */
34122         "layout" : true,
34123         /**
34124          * @event regionresized
34125          * Fires when the user resizes a region.
34126          * @param {Roo.LayoutRegion} region The resized region
34127          * @param {Number} newSize The new size (width for east/west, height for north/south)
34128          */
34129         "regionresized" : true,
34130         /**
34131          * @event regioncollapsed
34132          * Fires when a region is collapsed.
34133          * @param {Roo.LayoutRegion} region The collapsed region
34134          */
34135         "regioncollapsed" : true,
34136         /**
34137          * @event regionexpanded
34138          * Fires when a region is expanded.
34139          * @param {Roo.LayoutRegion} region The expanded region
34140          */
34141         "regionexpanded" : true
34142     });
34143     this.updating = false;
34144
34145     if (config.el) {
34146         this.el = Roo.get(config.el);
34147         this.initEvents();
34148     }
34149
34150 };
34151
34152 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34153
34154
34155     regions : null,
34156
34157     monitorWindowResize : true,
34158
34159
34160     updating : false,
34161
34162
34163     onRender : function(ct, position)
34164     {
34165         if(!this.el){
34166             this.el = Roo.get(ct);
34167             this.initEvents();
34168         }
34169         //this.fireEvent('render',this);
34170     },
34171
34172
34173     initEvents: function()
34174     {
34175
34176
34177         // ie scrollbar fix
34178         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34179             document.body.scroll = "no";
34180         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34181             this.el.position('relative');
34182         }
34183         this.id = this.el.id;
34184         this.el.addClass("roo-layout-container");
34185         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34186         if(this.el.dom != document.body ) {
34187             this.el.on('resize', this.layout,this);
34188             this.el.on('show', this.layout,this);
34189         }
34190
34191     },
34192
34193     /**
34194      * Returns true if this layout is currently being updated
34195      * @return {Boolean}
34196      */
34197     isUpdating : function(){
34198         return this.updating;
34199     },
34200
34201     /**
34202      * Suspend the LayoutManager from doing auto-layouts while
34203      * making multiple add or remove calls
34204      */
34205     beginUpdate : function(){
34206         this.updating = true;
34207     },
34208
34209     /**
34210      * Restore auto-layouts and optionally disable the manager from performing a layout
34211      * @param {Boolean} noLayout true to disable a layout update
34212      */
34213     endUpdate : function(noLayout){
34214         this.updating = false;
34215         if(!noLayout){
34216             this.layout();
34217         }
34218     },
34219
34220     layout: function(){
34221         // abstract...
34222     },
34223
34224     onRegionResized : function(region, newSize){
34225         this.fireEvent("regionresized", region, newSize);
34226         this.layout();
34227     },
34228
34229     onRegionCollapsed : function(region){
34230         this.fireEvent("regioncollapsed", region);
34231     },
34232
34233     onRegionExpanded : function(region){
34234         this.fireEvent("regionexpanded", region);
34235     },
34236
34237     /**
34238      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34239      * performs box-model adjustments.
34240      * @return {Object} The size as an object {width: (the width), height: (the height)}
34241      */
34242     getViewSize : function()
34243     {
34244         var size;
34245         if(this.el.dom != document.body){
34246             size = this.el.getSize();
34247         }else{
34248             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34249         }
34250         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34251         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34252         return size;
34253     },
34254
34255     /**
34256      * Returns the Element this layout is bound to.
34257      * @return {Roo.Element}
34258      */
34259     getEl : function(){
34260         return this.el;
34261     },
34262
34263     /**
34264      * Returns the specified region.
34265      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34266      * @return {Roo.LayoutRegion}
34267      */
34268     getRegion : function(target){
34269         return this.regions[target.toLowerCase()];
34270     },
34271
34272     onWindowResize : function(){
34273         if(this.monitorWindowResize){
34274             this.layout();
34275         }
34276     }
34277 });
34278 /*
34279  * Based on:
34280  * Ext JS Library 1.1.1
34281  * Copyright(c) 2006-2007, Ext JS, LLC.
34282  *
34283  * Originally Released Under LGPL - original licence link has changed is not relivant.
34284  *
34285  * Fork - LGPL
34286  * <script type="text/javascript">
34287  */
34288 /**
34289  * @class Roo.bootstrap.layout.Border
34290  * @extends Roo.bootstrap.layout.Manager
34291  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34292  * please see: examples/bootstrap/nested.html<br><br>
34293  
34294 <b>The container the layout is rendered into can be either the body element or any other element.
34295 If it is not the body element, the container needs to either be an absolute positioned element,
34296 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34297 the container size if it is not the body element.</b>
34298
34299 * @constructor
34300 * Create a new Border
34301 * @param {Object} config Configuration options
34302  */
34303 Roo.bootstrap.layout.Border = function(config){
34304     config = config || {};
34305     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34306     
34307     
34308     
34309     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34310         if(config[region]){
34311             config[region].region = region;
34312             this.addRegion(config[region]);
34313         }
34314     },this);
34315     
34316 };
34317
34318 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34319
34320 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34321     /**
34322      * Creates and adds a new region if it doesn't already exist.
34323      * @param {String} target The target region key (north, south, east, west or center).
34324      * @param {Object} config The regions config object
34325      * @return {BorderLayoutRegion} The new region
34326      */
34327     addRegion : function(config)
34328     {
34329         if(!this.regions[config.region]){
34330             var r = this.factory(config);
34331             this.bindRegion(r);
34332         }
34333         return this.regions[config.region];
34334     },
34335
34336     // private (kinda)
34337     bindRegion : function(r){
34338         this.regions[r.config.region] = r;
34339         
34340         r.on("visibilitychange",    this.layout, this);
34341         r.on("paneladded",          this.layout, this);
34342         r.on("panelremoved",        this.layout, this);
34343         r.on("invalidated",         this.layout, this);
34344         r.on("resized",             this.onRegionResized, this);
34345         r.on("collapsed",           this.onRegionCollapsed, this);
34346         r.on("expanded",            this.onRegionExpanded, this);
34347     },
34348
34349     /**
34350      * Performs a layout update.
34351      */
34352     layout : function()
34353     {
34354         if(this.updating) {
34355             return;
34356         }
34357         
34358         // render all the rebions if they have not been done alreayd?
34359         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34360             if(this.regions[region] && !this.regions[region].bodyEl){
34361                 this.regions[region].onRender(this.el)
34362             }
34363         },this);
34364         
34365         var size = this.getViewSize();
34366         var w = size.width;
34367         var h = size.height;
34368         var centerW = w;
34369         var centerH = h;
34370         var centerY = 0;
34371         var centerX = 0;
34372         //var x = 0, y = 0;
34373
34374         var rs = this.regions;
34375         var north = rs["north"];
34376         var south = rs["south"]; 
34377         var west = rs["west"];
34378         var east = rs["east"];
34379         var center = rs["center"];
34380         //if(this.hideOnLayout){ // not supported anymore
34381             //c.el.setStyle("display", "none");
34382         //}
34383         if(north && north.isVisible()){
34384             var b = north.getBox();
34385             var m = north.getMargins();
34386             b.width = w - (m.left+m.right);
34387             b.x = m.left;
34388             b.y = m.top;
34389             centerY = b.height + b.y + m.bottom;
34390             centerH -= centerY;
34391             north.updateBox(this.safeBox(b));
34392         }
34393         if(south && south.isVisible()){
34394             var b = south.getBox();
34395             var m = south.getMargins();
34396             b.width = w - (m.left+m.right);
34397             b.x = m.left;
34398             var totalHeight = (b.height + m.top + m.bottom);
34399             b.y = h - totalHeight + m.top;
34400             centerH -= totalHeight;
34401             south.updateBox(this.safeBox(b));
34402         }
34403         if(west && west.isVisible()){
34404             var b = west.getBox();
34405             var m = west.getMargins();
34406             b.height = centerH - (m.top+m.bottom);
34407             b.x = m.left;
34408             b.y = centerY + m.top;
34409             var totalWidth = (b.width + m.left + m.right);
34410             centerX += totalWidth;
34411             centerW -= totalWidth;
34412             west.updateBox(this.safeBox(b));
34413         }
34414         if(east && east.isVisible()){
34415             var b = east.getBox();
34416             var m = east.getMargins();
34417             b.height = centerH - (m.top+m.bottom);
34418             var totalWidth = (b.width + m.left + m.right);
34419             b.x = w - totalWidth + m.left;
34420             b.y = centerY + m.top;
34421             centerW -= totalWidth;
34422             east.updateBox(this.safeBox(b));
34423         }
34424         if(center){
34425             var m = center.getMargins();
34426             var centerBox = {
34427                 x: centerX + m.left,
34428                 y: centerY + m.top,
34429                 width: centerW - (m.left+m.right),
34430                 height: centerH - (m.top+m.bottom)
34431             };
34432             //if(this.hideOnLayout){
34433                 //center.el.setStyle("display", "block");
34434             //}
34435             center.updateBox(this.safeBox(centerBox));
34436         }
34437         this.el.repaint();
34438         this.fireEvent("layout", this);
34439     },
34440
34441     // private
34442     safeBox : function(box){
34443         box.width = Math.max(0, box.width);
34444         box.height = Math.max(0, box.height);
34445         return box;
34446     },
34447
34448     /**
34449      * Adds a ContentPanel (or subclass) to this layout.
34450      * @param {String} target The target region key (north, south, east, west or center).
34451      * @param {Roo.ContentPanel} panel The panel to add
34452      * @return {Roo.ContentPanel} The added panel
34453      */
34454     add : function(target, panel){
34455          
34456         target = target.toLowerCase();
34457         return this.regions[target].add(panel);
34458     },
34459
34460     /**
34461      * Remove a ContentPanel (or subclass) to this layout.
34462      * @param {String} target The target region key (north, south, east, west or center).
34463      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34464      * @return {Roo.ContentPanel} The removed panel
34465      */
34466     remove : function(target, panel){
34467         target = target.toLowerCase();
34468         return this.regions[target].remove(panel);
34469     },
34470
34471     /**
34472      * Searches all regions for a panel with the specified id
34473      * @param {String} panelId
34474      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34475      */
34476     findPanel : function(panelId){
34477         var rs = this.regions;
34478         for(var target in rs){
34479             if(typeof rs[target] != "function"){
34480                 var p = rs[target].getPanel(panelId);
34481                 if(p){
34482                     return p;
34483                 }
34484             }
34485         }
34486         return null;
34487     },
34488
34489     /**
34490      * Searches all regions for a panel with the specified id and activates (shows) it.
34491      * @param {String/ContentPanel} panelId The panels id or the panel itself
34492      * @return {Roo.ContentPanel} The shown panel or null
34493      */
34494     showPanel : function(panelId) {
34495       var rs = this.regions;
34496       for(var target in rs){
34497          var r = rs[target];
34498          if(typeof r != "function"){
34499             if(r.hasPanel(panelId)){
34500                return r.showPanel(panelId);
34501             }
34502          }
34503       }
34504       return null;
34505    },
34506
34507    /**
34508      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34509      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34510      */
34511    /*
34512     restoreState : function(provider){
34513         if(!provider){
34514             provider = Roo.state.Manager;
34515         }
34516         var sm = new Roo.LayoutStateManager();
34517         sm.init(this, provider);
34518     },
34519 */
34520  
34521  
34522     /**
34523      * Adds a xtype elements to the layout.
34524      * <pre><code>
34525
34526 layout.addxtype({
34527        xtype : 'ContentPanel',
34528        region: 'west',
34529        items: [ .... ]
34530    }
34531 );
34532
34533 layout.addxtype({
34534         xtype : 'NestedLayoutPanel',
34535         region: 'west',
34536         layout: {
34537            center: { },
34538            west: { }   
34539         },
34540         items : [ ... list of content panels or nested layout panels.. ]
34541    }
34542 );
34543 </code></pre>
34544      * @param {Object} cfg Xtype definition of item to add.
34545      */
34546     addxtype : function(cfg)
34547     {
34548         // basically accepts a pannel...
34549         // can accept a layout region..!?!?
34550         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34551         
34552         
34553         // theory?  children can only be panels??
34554         
34555         //if (!cfg.xtype.match(/Panel$/)) {
34556         //    return false;
34557         //}
34558         var ret = false;
34559         
34560         if (typeof(cfg.region) == 'undefined') {
34561             Roo.log("Failed to add Panel, region was not set");
34562             Roo.log(cfg);
34563             return false;
34564         }
34565         var region = cfg.region;
34566         delete cfg.region;
34567         
34568           
34569         var xitems = [];
34570         if (cfg.items) {
34571             xitems = cfg.items;
34572             delete cfg.items;
34573         }
34574         var nb = false;
34575         
34576         switch(cfg.xtype) 
34577         {
34578             case 'Content':  // ContentPanel (el, cfg)
34579             case 'Scroll':  // ContentPanel (el, cfg)
34580             case 'View': 
34581                 cfg.autoCreate = true;
34582                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34583                 //} else {
34584                 //    var el = this.el.createChild();
34585                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34586                 //}
34587                 
34588                 this.add(region, ret);
34589                 break;
34590             
34591             /*
34592             case 'TreePanel': // our new panel!
34593                 cfg.el = this.el.createChild();
34594                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34595                 this.add(region, ret);
34596                 break;
34597             */
34598             
34599             case 'Nest': 
34600                 // create a new Layout (which is  a Border Layout...
34601                 
34602                 var clayout = cfg.layout;
34603                 clayout.el  = this.el.createChild();
34604                 clayout.items   = clayout.items  || [];
34605                 
34606                 delete cfg.layout;
34607                 
34608                 // replace this exitems with the clayout ones..
34609                 xitems = clayout.items;
34610                  
34611                 // force background off if it's in center...
34612                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34613                     cfg.background = false;
34614                 }
34615                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34616                 
34617                 
34618                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34619                 //console.log('adding nested layout panel '  + cfg.toSource());
34620                 this.add(region, ret);
34621                 nb = {}; /// find first...
34622                 break;
34623             
34624             case 'Grid':
34625                 
34626                 // needs grid and region
34627                 
34628                 //var el = this.getRegion(region).el.createChild();
34629                 /*
34630                  *var el = this.el.createChild();
34631                 // create the grid first...
34632                 cfg.grid.container = el;
34633                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34634                 */
34635                 
34636                 if (region == 'center' && this.active ) {
34637                     cfg.background = false;
34638                 }
34639                 
34640                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34641                 
34642                 this.add(region, ret);
34643                 /*
34644                 if (cfg.background) {
34645                     // render grid on panel activation (if panel background)
34646                     ret.on('activate', function(gp) {
34647                         if (!gp.grid.rendered) {
34648                     //        gp.grid.render(el);
34649                         }
34650                     });
34651                 } else {
34652                   //  cfg.grid.render(el);
34653                 }
34654                 */
34655                 break;
34656            
34657            
34658             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34659                 // it was the old xcomponent building that caused this before.
34660                 // espeically if border is the top element in the tree.
34661                 ret = this;
34662                 break; 
34663                 
34664                     
34665                 
34666                 
34667                 
34668             default:
34669                 /*
34670                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34671                     
34672                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34673                     this.add(region, ret);
34674                 } else {
34675                 */
34676                     Roo.log(cfg);
34677                     throw "Can not add '" + cfg.xtype + "' to Border";
34678                     return null;
34679              
34680                                 
34681              
34682         }
34683         this.beginUpdate();
34684         // add children..
34685         var region = '';
34686         var abn = {};
34687         Roo.each(xitems, function(i)  {
34688             region = nb && i.region ? i.region : false;
34689             
34690             var add = ret.addxtype(i);
34691            
34692             if (region) {
34693                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34694                 if (!i.background) {
34695                     abn[region] = nb[region] ;
34696                 }
34697             }
34698             
34699         });
34700         this.endUpdate();
34701
34702         // make the last non-background panel active..
34703         //if (nb) { Roo.log(abn); }
34704         if (nb) {
34705             
34706             for(var r in abn) {
34707                 region = this.getRegion(r);
34708                 if (region) {
34709                     // tried using nb[r], but it does not work..
34710                      
34711                     region.showPanel(abn[r]);
34712                    
34713                 }
34714             }
34715         }
34716         return ret;
34717         
34718     },
34719     
34720     
34721 // private
34722     factory : function(cfg)
34723     {
34724         
34725         var validRegions = Roo.bootstrap.layout.Border.regions;
34726
34727         var target = cfg.region;
34728         cfg.mgr = this;
34729         
34730         var r = Roo.bootstrap.layout;
34731         Roo.log(target);
34732         switch(target){
34733             case "north":
34734                 return new r.North(cfg);
34735             case "south":
34736                 return new r.South(cfg);
34737             case "east":
34738                 return new r.East(cfg);
34739             case "west":
34740                 return new r.West(cfg);
34741             case "center":
34742                 return new r.Center(cfg);
34743         }
34744         throw 'Layout region "'+target+'" not supported.';
34745     }
34746     
34747     
34748 });
34749  /*
34750  * Based on:
34751  * Ext JS Library 1.1.1
34752  * Copyright(c) 2006-2007, Ext JS, LLC.
34753  *
34754  * Originally Released Under LGPL - original licence link has changed is not relivant.
34755  *
34756  * Fork - LGPL
34757  * <script type="text/javascript">
34758  */
34759  
34760 /**
34761  * @class Roo.bootstrap.layout.Basic
34762  * @extends Roo.util.Observable
34763  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34764  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34765  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34766  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34767  * @cfg {string}   region  the region that it inhabits..
34768  * @cfg {bool}   skipConfig skip config?
34769  * 
34770
34771  */
34772 Roo.bootstrap.layout.Basic = function(config){
34773     
34774     this.mgr = config.mgr;
34775     
34776     this.position = config.region;
34777     
34778     var skipConfig = config.skipConfig;
34779     
34780     this.events = {
34781         /**
34782          * @scope Roo.BasicLayoutRegion
34783          */
34784         
34785         /**
34786          * @event beforeremove
34787          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34788          * @param {Roo.LayoutRegion} this
34789          * @param {Roo.ContentPanel} panel The panel
34790          * @param {Object} e The cancel event object
34791          */
34792         "beforeremove" : true,
34793         /**
34794          * @event invalidated
34795          * Fires when the layout for this region is changed.
34796          * @param {Roo.LayoutRegion} this
34797          */
34798         "invalidated" : true,
34799         /**
34800          * @event visibilitychange
34801          * Fires when this region is shown or hidden 
34802          * @param {Roo.LayoutRegion} this
34803          * @param {Boolean} visibility true or false
34804          */
34805         "visibilitychange" : true,
34806         /**
34807          * @event paneladded
34808          * Fires when a panel is added. 
34809          * @param {Roo.LayoutRegion} this
34810          * @param {Roo.ContentPanel} panel The panel
34811          */
34812         "paneladded" : true,
34813         /**
34814          * @event panelremoved
34815          * Fires when a panel is removed. 
34816          * @param {Roo.LayoutRegion} this
34817          * @param {Roo.ContentPanel} panel The panel
34818          */
34819         "panelremoved" : true,
34820         /**
34821          * @event beforecollapse
34822          * Fires when this region before collapse.
34823          * @param {Roo.LayoutRegion} this
34824          */
34825         "beforecollapse" : true,
34826         /**
34827          * @event collapsed
34828          * Fires when this region is collapsed.
34829          * @param {Roo.LayoutRegion} this
34830          */
34831         "collapsed" : true,
34832         /**
34833          * @event expanded
34834          * Fires when this region is expanded.
34835          * @param {Roo.LayoutRegion} this
34836          */
34837         "expanded" : true,
34838         /**
34839          * @event slideshow
34840          * Fires when this region is slid into view.
34841          * @param {Roo.LayoutRegion} this
34842          */
34843         "slideshow" : true,
34844         /**
34845          * @event slidehide
34846          * Fires when this region slides out of view. 
34847          * @param {Roo.LayoutRegion} this
34848          */
34849         "slidehide" : true,
34850         /**
34851          * @event panelactivated
34852          * Fires when a panel is activated. 
34853          * @param {Roo.LayoutRegion} this
34854          * @param {Roo.ContentPanel} panel The activated panel
34855          */
34856         "panelactivated" : true,
34857         /**
34858          * @event resized
34859          * Fires when the user resizes this region. 
34860          * @param {Roo.LayoutRegion} this
34861          * @param {Number} newSize The new size (width for east/west, height for north/south)
34862          */
34863         "resized" : true
34864     };
34865     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34866     this.panels = new Roo.util.MixedCollection();
34867     this.panels.getKey = this.getPanelId.createDelegate(this);
34868     this.box = null;
34869     this.activePanel = null;
34870     // ensure listeners are added...
34871     
34872     if (config.listeners || config.events) {
34873         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34874             listeners : config.listeners || {},
34875             events : config.events || {}
34876         });
34877     }
34878     
34879     if(skipConfig !== true){
34880         this.applyConfig(config);
34881     }
34882 };
34883
34884 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34885 {
34886     getPanelId : function(p){
34887         return p.getId();
34888     },
34889     
34890     applyConfig : function(config){
34891         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34892         this.config = config;
34893         
34894     },
34895     
34896     /**
34897      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34898      * the width, for horizontal (north, south) the height.
34899      * @param {Number} newSize The new width or height
34900      */
34901     resizeTo : function(newSize){
34902         var el = this.el ? this.el :
34903                  (this.activePanel ? this.activePanel.getEl() : null);
34904         if(el){
34905             switch(this.position){
34906                 case "east":
34907                 case "west":
34908                     el.setWidth(newSize);
34909                     this.fireEvent("resized", this, newSize);
34910                 break;
34911                 case "north":
34912                 case "south":
34913                     el.setHeight(newSize);
34914                     this.fireEvent("resized", this, newSize);
34915                 break;                
34916             }
34917         }
34918     },
34919     
34920     getBox : function(){
34921         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34922     },
34923     
34924     getMargins : function(){
34925         return this.margins;
34926     },
34927     
34928     updateBox : function(box){
34929         this.box = box;
34930         var el = this.activePanel.getEl();
34931         el.dom.style.left = box.x + "px";
34932         el.dom.style.top = box.y + "px";
34933         this.activePanel.setSize(box.width, box.height);
34934     },
34935     
34936     /**
34937      * Returns the container element for this region.
34938      * @return {Roo.Element}
34939      */
34940     getEl : function(){
34941         return this.activePanel;
34942     },
34943     
34944     /**
34945      * Returns true if this region is currently visible.
34946      * @return {Boolean}
34947      */
34948     isVisible : function(){
34949         return this.activePanel ? true : false;
34950     },
34951     
34952     setActivePanel : function(panel){
34953         panel = this.getPanel(panel);
34954         if(this.activePanel && this.activePanel != panel){
34955             this.activePanel.setActiveState(false);
34956             this.activePanel.getEl().setLeftTop(-10000,-10000);
34957         }
34958         this.activePanel = panel;
34959         panel.setActiveState(true);
34960         if(this.box){
34961             panel.setSize(this.box.width, this.box.height);
34962         }
34963         this.fireEvent("panelactivated", this, panel);
34964         this.fireEvent("invalidated");
34965     },
34966     
34967     /**
34968      * Show the specified panel.
34969      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34970      * @return {Roo.ContentPanel} The shown panel or null
34971      */
34972     showPanel : function(panel){
34973         panel = this.getPanel(panel);
34974         if(panel){
34975             this.setActivePanel(panel);
34976         }
34977         return panel;
34978     },
34979     
34980     /**
34981      * Get the active panel for this region.
34982      * @return {Roo.ContentPanel} The active panel or null
34983      */
34984     getActivePanel : function(){
34985         return this.activePanel;
34986     },
34987     
34988     /**
34989      * Add the passed ContentPanel(s)
34990      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34991      * @return {Roo.ContentPanel} The panel added (if only one was added)
34992      */
34993     add : function(panel){
34994         if(arguments.length > 1){
34995             for(var i = 0, len = arguments.length; i < len; i++) {
34996                 this.add(arguments[i]);
34997             }
34998             return null;
34999         }
35000         if(this.hasPanel(panel)){
35001             this.showPanel(panel);
35002             return panel;
35003         }
35004         var el = panel.getEl();
35005         if(el.dom.parentNode != this.mgr.el.dom){
35006             this.mgr.el.dom.appendChild(el.dom);
35007         }
35008         if(panel.setRegion){
35009             panel.setRegion(this);
35010         }
35011         this.panels.add(panel);
35012         el.setStyle("position", "absolute");
35013         if(!panel.background){
35014             this.setActivePanel(panel);
35015             if(this.config.initialSize && this.panels.getCount()==1){
35016                 this.resizeTo(this.config.initialSize);
35017             }
35018         }
35019         this.fireEvent("paneladded", this, panel);
35020         return panel;
35021     },
35022     
35023     /**
35024      * Returns true if the panel is in this region.
35025      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35026      * @return {Boolean}
35027      */
35028     hasPanel : function(panel){
35029         if(typeof panel == "object"){ // must be panel obj
35030             panel = panel.getId();
35031         }
35032         return this.getPanel(panel) ? true : false;
35033     },
35034     
35035     /**
35036      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35037      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35038      * @param {Boolean} preservePanel Overrides the config preservePanel option
35039      * @return {Roo.ContentPanel} The panel that was removed
35040      */
35041     remove : function(panel, preservePanel){
35042         panel = this.getPanel(panel);
35043         if(!panel){
35044             return null;
35045         }
35046         var e = {};
35047         this.fireEvent("beforeremove", this, panel, e);
35048         if(e.cancel === true){
35049             return null;
35050         }
35051         var panelId = panel.getId();
35052         this.panels.removeKey(panelId);
35053         return panel;
35054     },
35055     
35056     /**
35057      * Returns the panel specified or null if it's not in this region.
35058      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35059      * @return {Roo.ContentPanel}
35060      */
35061     getPanel : function(id){
35062         if(typeof id == "object"){ // must be panel obj
35063             return id;
35064         }
35065         return this.panels.get(id);
35066     },
35067     
35068     /**
35069      * Returns this regions position (north/south/east/west/center).
35070      * @return {String} 
35071      */
35072     getPosition: function(){
35073         return this.position;    
35074     }
35075 });/*
35076  * Based on:
35077  * Ext JS Library 1.1.1
35078  * Copyright(c) 2006-2007, Ext JS, LLC.
35079  *
35080  * Originally Released Under LGPL - original licence link has changed is not relivant.
35081  *
35082  * Fork - LGPL
35083  * <script type="text/javascript">
35084  */
35085  
35086 /**
35087  * @class Roo.bootstrap.layout.Region
35088  * @extends Roo.bootstrap.layout.Basic
35089  * This class represents a region in a layout manager.
35090  
35091  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35092  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
35093  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35094  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35095  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35096  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35097  * @cfg {String}    title           The title for the region (overrides panel titles)
35098  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35099  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35100  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35101  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35102  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35103  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35104  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35105  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35106  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35107  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35108
35109  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35110  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35111  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35112  * @cfg {Number}    width           For East/West panels
35113  * @cfg {Number}    height          For North/South panels
35114  * @cfg {Boolean}   split           To show the splitter
35115  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35116  * 
35117  * @cfg {string}   cls             Extra CSS classes to add to region
35118  * 
35119  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35120  * @cfg {string}   region  the region that it inhabits..
35121  *
35122
35123  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35124  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35125
35126  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35127  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35128  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35129  */
35130 Roo.bootstrap.layout.Region = function(config)
35131 {
35132     this.applyConfig(config);
35133
35134     var mgr = config.mgr;
35135     var pos = config.region;
35136     config.skipConfig = true;
35137     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35138     
35139     if (mgr.el) {
35140         this.onRender(mgr.el);   
35141     }
35142      
35143     this.visible = true;
35144     this.collapsed = false;
35145     this.unrendered_panels = [];
35146 };
35147
35148 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35149
35150     position: '', // set by wrapper (eg. north/south etc..)
35151     unrendered_panels : null,  // unrendered panels.
35152     createBody : function(){
35153         /** This region's body element 
35154         * @type Roo.Element */
35155         this.bodyEl = this.el.createChild({
35156                 tag: "div",
35157                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35158         });
35159     },
35160
35161     onRender: function(ctr, pos)
35162     {
35163         var dh = Roo.DomHelper;
35164         /** This region's container element 
35165         * @type Roo.Element */
35166         this.el = dh.append(ctr.dom, {
35167                 tag: "div",
35168                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35169             }, true);
35170         /** This region's title element 
35171         * @type Roo.Element */
35172     
35173         this.titleEl = dh.append(this.el.dom,
35174             {
35175                     tag: "div",
35176                     unselectable: "on",
35177                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35178                     children:[
35179                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35180                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35181                     ]}, true);
35182         
35183         this.titleEl.enableDisplayMode();
35184         /** This region's title text element 
35185         * @type HTMLElement */
35186         this.titleTextEl = this.titleEl.dom.firstChild;
35187         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35188         /*
35189         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35190         this.closeBtn.enableDisplayMode();
35191         this.closeBtn.on("click", this.closeClicked, this);
35192         this.closeBtn.hide();
35193     */
35194         this.createBody(this.config);
35195         if(this.config.hideWhenEmpty){
35196             this.hide();
35197             this.on("paneladded", this.validateVisibility, this);
35198             this.on("panelremoved", this.validateVisibility, this);
35199         }
35200         if(this.autoScroll){
35201             this.bodyEl.setStyle("overflow", "auto");
35202         }else{
35203             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35204         }
35205         //if(c.titlebar !== false){
35206             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35207                 this.titleEl.hide();
35208             }else{
35209                 this.titleEl.show();
35210                 if(this.config.title){
35211                     this.titleTextEl.innerHTML = this.config.title;
35212                 }
35213             }
35214         //}
35215         if(this.config.collapsed){
35216             this.collapse(true);
35217         }
35218         if(this.config.hidden){
35219             this.hide();
35220         }
35221         
35222         if (this.unrendered_panels && this.unrendered_panels.length) {
35223             for (var i =0;i< this.unrendered_panels.length; i++) {
35224                 this.add(this.unrendered_panels[i]);
35225             }
35226             this.unrendered_panels = null;
35227             
35228         }
35229         
35230     },
35231     
35232     applyConfig : function(c)
35233     {
35234         /*
35235          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35236             var dh = Roo.DomHelper;
35237             if(c.titlebar !== false){
35238                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35239                 this.collapseBtn.on("click", this.collapse, this);
35240                 this.collapseBtn.enableDisplayMode();
35241                 /*
35242                 if(c.showPin === true || this.showPin){
35243                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35244                     this.stickBtn.enableDisplayMode();
35245                     this.stickBtn.on("click", this.expand, this);
35246                     this.stickBtn.hide();
35247                 }
35248                 
35249             }
35250             */
35251             /** This region's collapsed element
35252             * @type Roo.Element */
35253             /*
35254              *
35255             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35256                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35257             ]}, true);
35258             
35259             if(c.floatable !== false){
35260                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35261                this.collapsedEl.on("click", this.collapseClick, this);
35262             }
35263
35264             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35265                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35266                    id: "message", unselectable: "on", style:{"float":"left"}});
35267                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35268              }
35269             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35270             this.expandBtn.on("click", this.expand, this);
35271             
35272         }
35273         
35274         if(this.collapseBtn){
35275             this.collapseBtn.setVisible(c.collapsible == true);
35276         }
35277         
35278         this.cmargins = c.cmargins || this.cmargins ||
35279                          (this.position == "west" || this.position == "east" ?
35280                              {top: 0, left: 2, right:2, bottom: 0} :
35281                              {top: 2, left: 0, right:0, bottom: 2});
35282         */
35283         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35284         
35285         
35286         this.bottomTabs = c.tabPosition != "top";
35287         
35288         this.autoScroll = c.autoScroll || false;
35289         
35290         
35291        
35292         
35293         this.duration = c.duration || .30;
35294         this.slideDuration = c.slideDuration || .45;
35295         this.config = c;
35296        
35297     },
35298     /**
35299      * Returns true if this region is currently visible.
35300      * @return {Boolean}
35301      */
35302     isVisible : function(){
35303         return this.visible;
35304     },
35305
35306     /**
35307      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35308      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35309      */
35310     //setCollapsedTitle : function(title){
35311     //    title = title || "&#160;";
35312      //   if(this.collapsedTitleTextEl){
35313       //      this.collapsedTitleTextEl.innerHTML = title;
35314        // }
35315     //},
35316
35317     getBox : function(){
35318         var b;
35319       //  if(!this.collapsed){
35320             b = this.el.getBox(false, true);
35321        // }else{
35322           //  b = this.collapsedEl.getBox(false, true);
35323         //}
35324         return b;
35325     },
35326
35327     getMargins : function(){
35328         return this.margins;
35329         //return this.collapsed ? this.cmargins : this.margins;
35330     },
35331 /*
35332     highlight : function(){
35333         this.el.addClass("x-layout-panel-dragover");
35334     },
35335
35336     unhighlight : function(){
35337         this.el.removeClass("x-layout-panel-dragover");
35338     },
35339 */
35340     updateBox : function(box)
35341     {
35342         if (!this.bodyEl) {
35343             return; // not rendered yet..
35344         }
35345         
35346         this.box = box;
35347         if(!this.collapsed){
35348             this.el.dom.style.left = box.x + "px";
35349             this.el.dom.style.top = box.y + "px";
35350             this.updateBody(box.width, box.height);
35351         }else{
35352             this.collapsedEl.dom.style.left = box.x + "px";
35353             this.collapsedEl.dom.style.top = box.y + "px";
35354             this.collapsedEl.setSize(box.width, box.height);
35355         }
35356         if(this.tabs){
35357             this.tabs.autoSizeTabs();
35358         }
35359     },
35360
35361     updateBody : function(w, h)
35362     {
35363         if(w !== null){
35364             this.el.setWidth(w);
35365             w -= this.el.getBorderWidth("rl");
35366             if(this.config.adjustments){
35367                 w += this.config.adjustments[0];
35368             }
35369         }
35370         if(h !== null && h > 0){
35371             this.el.setHeight(h);
35372             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35373             h -= this.el.getBorderWidth("tb");
35374             if(this.config.adjustments){
35375                 h += this.config.adjustments[1];
35376             }
35377             this.bodyEl.setHeight(h);
35378             if(this.tabs){
35379                 h = this.tabs.syncHeight(h);
35380             }
35381         }
35382         if(this.panelSize){
35383             w = w !== null ? w : this.panelSize.width;
35384             h = h !== null ? h : this.panelSize.height;
35385         }
35386         if(this.activePanel){
35387             var el = this.activePanel.getEl();
35388             w = w !== null ? w : el.getWidth();
35389             h = h !== null ? h : el.getHeight();
35390             this.panelSize = {width: w, height: h};
35391             this.activePanel.setSize(w, h);
35392         }
35393         if(Roo.isIE && this.tabs){
35394             this.tabs.el.repaint();
35395         }
35396     },
35397
35398     /**
35399      * Returns the container element for this region.
35400      * @return {Roo.Element}
35401      */
35402     getEl : function(){
35403         return this.el;
35404     },
35405
35406     /**
35407      * Hides this region.
35408      */
35409     hide : function(){
35410         //if(!this.collapsed){
35411             this.el.dom.style.left = "-2000px";
35412             this.el.hide();
35413         //}else{
35414          //   this.collapsedEl.dom.style.left = "-2000px";
35415          //   this.collapsedEl.hide();
35416        // }
35417         this.visible = false;
35418         this.fireEvent("visibilitychange", this, false);
35419     },
35420
35421     /**
35422      * Shows this region if it was previously hidden.
35423      */
35424     show : function(){
35425         //if(!this.collapsed){
35426             this.el.show();
35427         //}else{
35428         //    this.collapsedEl.show();
35429        // }
35430         this.visible = true;
35431         this.fireEvent("visibilitychange", this, true);
35432     },
35433 /*
35434     closeClicked : function(){
35435         if(this.activePanel){
35436             this.remove(this.activePanel);
35437         }
35438     },
35439
35440     collapseClick : function(e){
35441         if(this.isSlid){
35442            e.stopPropagation();
35443            this.slideIn();
35444         }else{
35445            e.stopPropagation();
35446            this.slideOut();
35447         }
35448     },
35449 */
35450     /**
35451      * Collapses this region.
35452      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35453      */
35454     /*
35455     collapse : function(skipAnim, skipCheck = false){
35456         if(this.collapsed) {
35457             return;
35458         }
35459         
35460         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35461             
35462             this.collapsed = true;
35463             if(this.split){
35464                 this.split.el.hide();
35465             }
35466             if(this.config.animate && skipAnim !== true){
35467                 this.fireEvent("invalidated", this);
35468                 this.animateCollapse();
35469             }else{
35470                 this.el.setLocation(-20000,-20000);
35471                 this.el.hide();
35472                 this.collapsedEl.show();
35473                 this.fireEvent("collapsed", this);
35474                 this.fireEvent("invalidated", this);
35475             }
35476         }
35477         
35478     },
35479 */
35480     animateCollapse : function(){
35481         // overridden
35482     },
35483
35484     /**
35485      * Expands this region if it was previously collapsed.
35486      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35487      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35488      */
35489     /*
35490     expand : function(e, skipAnim){
35491         if(e) {
35492             e.stopPropagation();
35493         }
35494         if(!this.collapsed || this.el.hasActiveFx()) {
35495             return;
35496         }
35497         if(this.isSlid){
35498             this.afterSlideIn();
35499             skipAnim = true;
35500         }
35501         this.collapsed = false;
35502         if(this.config.animate && skipAnim !== true){
35503             this.animateExpand();
35504         }else{
35505             this.el.show();
35506             if(this.split){
35507                 this.split.el.show();
35508             }
35509             this.collapsedEl.setLocation(-2000,-2000);
35510             this.collapsedEl.hide();
35511             this.fireEvent("invalidated", this);
35512             this.fireEvent("expanded", this);
35513         }
35514     },
35515 */
35516     animateExpand : function(){
35517         // overridden
35518     },
35519
35520     initTabs : function()
35521     {
35522         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35523         
35524         var ts = new Roo.bootstrap.panel.Tabs({
35525                 el: this.bodyEl.dom,
35526                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35527                 disableTooltips: this.config.disableTabTips,
35528                 toolbar : this.config.toolbar
35529             });
35530         
35531         if(this.config.hideTabs){
35532             ts.stripWrap.setDisplayed(false);
35533         }
35534         this.tabs = ts;
35535         ts.resizeTabs = this.config.resizeTabs === true;
35536         ts.minTabWidth = this.config.minTabWidth || 40;
35537         ts.maxTabWidth = this.config.maxTabWidth || 250;
35538         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35539         ts.monitorResize = false;
35540         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35541         ts.bodyEl.addClass('roo-layout-tabs-body');
35542         this.panels.each(this.initPanelAsTab, this);
35543     },
35544
35545     initPanelAsTab : function(panel){
35546         var ti = this.tabs.addTab(
35547             panel.getEl().id,
35548             panel.getTitle(),
35549             null,
35550             this.config.closeOnTab && panel.isClosable(),
35551             panel.tpl
35552         );
35553         if(panel.tabTip !== undefined){
35554             ti.setTooltip(panel.tabTip);
35555         }
35556         ti.on("activate", function(){
35557               this.setActivePanel(panel);
35558         }, this);
35559         
35560         if(this.config.closeOnTab){
35561             ti.on("beforeclose", function(t, e){
35562                 e.cancel = true;
35563                 this.remove(panel);
35564             }, this);
35565         }
35566         
35567         panel.tabItem = ti;
35568         
35569         return ti;
35570     },
35571
35572     updatePanelTitle : function(panel, title)
35573     {
35574         if(this.activePanel == panel){
35575             this.updateTitle(title);
35576         }
35577         if(this.tabs){
35578             var ti = this.tabs.getTab(panel.getEl().id);
35579             ti.setText(title);
35580             if(panel.tabTip !== undefined){
35581                 ti.setTooltip(panel.tabTip);
35582             }
35583         }
35584     },
35585
35586     updateTitle : function(title){
35587         if(this.titleTextEl && !this.config.title){
35588             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35589         }
35590     },
35591
35592     setActivePanel : function(panel)
35593     {
35594         panel = this.getPanel(panel);
35595         if(this.activePanel && this.activePanel != panel){
35596             if(this.activePanel.setActiveState(false) === false){
35597                 return;
35598             }
35599         }
35600         this.activePanel = panel;
35601         panel.setActiveState(true);
35602         if(this.panelSize){
35603             panel.setSize(this.panelSize.width, this.panelSize.height);
35604         }
35605         if(this.closeBtn){
35606             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35607         }
35608         this.updateTitle(panel.getTitle());
35609         if(this.tabs){
35610             this.fireEvent("invalidated", this);
35611         }
35612         this.fireEvent("panelactivated", this, panel);
35613     },
35614
35615     /**
35616      * Shows the specified panel.
35617      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35618      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35619      */
35620     showPanel : function(panel)
35621     {
35622         panel = this.getPanel(panel);
35623         if(panel){
35624             if(this.tabs){
35625                 var tab = this.tabs.getTab(panel.getEl().id);
35626                 if(tab.isHidden()){
35627                     this.tabs.unhideTab(tab.id);
35628                 }
35629                 tab.activate();
35630             }else{
35631                 this.setActivePanel(panel);
35632             }
35633         }
35634         return panel;
35635     },
35636
35637     /**
35638      * Get the active panel for this region.
35639      * @return {Roo.ContentPanel} The active panel or null
35640      */
35641     getActivePanel : function(){
35642         return this.activePanel;
35643     },
35644
35645     validateVisibility : function(){
35646         if(this.panels.getCount() < 1){
35647             this.updateTitle("&#160;");
35648             this.closeBtn.hide();
35649             this.hide();
35650         }else{
35651             if(!this.isVisible()){
35652                 this.show();
35653             }
35654         }
35655     },
35656
35657     /**
35658      * Adds the passed ContentPanel(s) to this region.
35659      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35660      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35661      */
35662     add : function(panel)
35663     {
35664         if(arguments.length > 1){
35665             for(var i = 0, len = arguments.length; i < len; i++) {
35666                 this.add(arguments[i]);
35667             }
35668             return null;
35669         }
35670         
35671         // if we have not been rendered yet, then we can not really do much of this..
35672         if (!this.bodyEl) {
35673             this.unrendered_panels.push(panel);
35674             return panel;
35675         }
35676         
35677         
35678         
35679         
35680         if(this.hasPanel(panel)){
35681             this.showPanel(panel);
35682             return panel;
35683         }
35684         panel.setRegion(this);
35685         this.panels.add(panel);
35686        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35687             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35688             // and hide them... ???
35689             this.bodyEl.dom.appendChild(panel.getEl().dom);
35690             if(panel.background !== true){
35691                 this.setActivePanel(panel);
35692             }
35693             this.fireEvent("paneladded", this, panel);
35694             return panel;
35695         }
35696         */
35697         if(!this.tabs){
35698             this.initTabs();
35699         }else{
35700             this.initPanelAsTab(panel);
35701         }
35702         
35703         
35704         if(panel.background !== true){
35705             this.tabs.activate(panel.getEl().id);
35706         }
35707         this.fireEvent("paneladded", this, panel);
35708         return panel;
35709     },
35710
35711     /**
35712      * Hides the tab for the specified panel.
35713      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35714      */
35715     hidePanel : function(panel){
35716         if(this.tabs && (panel = this.getPanel(panel))){
35717             this.tabs.hideTab(panel.getEl().id);
35718         }
35719     },
35720
35721     /**
35722      * Unhides the tab for a previously hidden panel.
35723      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35724      */
35725     unhidePanel : function(panel){
35726         if(this.tabs && (panel = this.getPanel(panel))){
35727             this.tabs.unhideTab(panel.getEl().id);
35728         }
35729     },
35730
35731     clearPanels : function(){
35732         while(this.panels.getCount() > 0){
35733              this.remove(this.panels.first());
35734         }
35735     },
35736
35737     /**
35738      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35739      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35740      * @param {Boolean} preservePanel Overrides the config preservePanel option
35741      * @return {Roo.ContentPanel} The panel that was removed
35742      */
35743     remove : function(panel, preservePanel)
35744     {
35745         panel = this.getPanel(panel);
35746         if(!panel){
35747             return null;
35748         }
35749         var e = {};
35750         this.fireEvent("beforeremove", this, panel, e);
35751         if(e.cancel === true){
35752             return null;
35753         }
35754         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35755         var panelId = panel.getId();
35756         this.panels.removeKey(panelId);
35757         if(preservePanel){
35758             document.body.appendChild(panel.getEl().dom);
35759         }
35760         if(this.tabs){
35761             this.tabs.removeTab(panel.getEl().id);
35762         }else if (!preservePanel){
35763             this.bodyEl.dom.removeChild(panel.getEl().dom);
35764         }
35765         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35766             var p = this.panels.first();
35767             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35768             tempEl.appendChild(p.getEl().dom);
35769             this.bodyEl.update("");
35770             this.bodyEl.dom.appendChild(p.getEl().dom);
35771             tempEl = null;
35772             this.updateTitle(p.getTitle());
35773             this.tabs = null;
35774             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35775             this.setActivePanel(p);
35776         }
35777         panel.setRegion(null);
35778         if(this.activePanel == panel){
35779             this.activePanel = null;
35780         }
35781         if(this.config.autoDestroy !== false && preservePanel !== true){
35782             try{panel.destroy();}catch(e){}
35783         }
35784         this.fireEvent("panelremoved", this, panel);
35785         return panel;
35786     },
35787
35788     /**
35789      * Returns the TabPanel component used by this region
35790      * @return {Roo.TabPanel}
35791      */
35792     getTabs : function(){
35793         return this.tabs;
35794     },
35795
35796     createTool : function(parentEl, className){
35797         var btn = Roo.DomHelper.append(parentEl, {
35798             tag: "div",
35799             cls: "x-layout-tools-button",
35800             children: [ {
35801                 tag: "div",
35802                 cls: "roo-layout-tools-button-inner " + className,
35803                 html: "&#160;"
35804             }]
35805         }, true);
35806         btn.addClassOnOver("roo-layout-tools-button-over");
35807         return btn;
35808     }
35809 });/*
35810  * Based on:
35811  * Ext JS Library 1.1.1
35812  * Copyright(c) 2006-2007, Ext JS, LLC.
35813  *
35814  * Originally Released Under LGPL - original licence link has changed is not relivant.
35815  *
35816  * Fork - LGPL
35817  * <script type="text/javascript">
35818  */
35819  
35820
35821
35822 /**
35823  * @class Roo.SplitLayoutRegion
35824  * @extends Roo.LayoutRegion
35825  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35826  */
35827 Roo.bootstrap.layout.Split = function(config){
35828     this.cursor = config.cursor;
35829     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35830 };
35831
35832 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35833 {
35834     splitTip : "Drag to resize.",
35835     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35836     useSplitTips : false,
35837
35838     applyConfig : function(config){
35839         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35840     },
35841     
35842     onRender : function(ctr,pos) {
35843         
35844         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35845         if(!this.config.split){
35846             return;
35847         }
35848         if(!this.split){
35849             
35850             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35851                             tag: "div",
35852                             id: this.el.id + "-split",
35853                             cls: "roo-layout-split roo-layout-split-"+this.position,
35854                             html: "&#160;"
35855             });
35856             /** The SplitBar for this region 
35857             * @type Roo.SplitBar */
35858             // does not exist yet...
35859             Roo.log([this.position, this.orientation]);
35860             
35861             this.split = new Roo.bootstrap.SplitBar({
35862                 dragElement : splitEl,
35863                 resizingElement: this.el,
35864                 orientation : this.orientation
35865             });
35866             
35867             this.split.on("moved", this.onSplitMove, this);
35868             this.split.useShim = this.config.useShim === true;
35869             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35870             if(this.useSplitTips){
35871                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35872             }
35873             //if(config.collapsible){
35874             //    this.split.el.on("dblclick", this.collapse,  this);
35875             //}
35876         }
35877         if(typeof this.config.minSize != "undefined"){
35878             this.split.minSize = this.config.minSize;
35879         }
35880         if(typeof this.config.maxSize != "undefined"){
35881             this.split.maxSize = this.config.maxSize;
35882         }
35883         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35884             this.hideSplitter();
35885         }
35886         
35887     },
35888
35889     getHMaxSize : function(){
35890          var cmax = this.config.maxSize || 10000;
35891          var center = this.mgr.getRegion("center");
35892          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35893     },
35894
35895     getVMaxSize : function(){
35896          var cmax = this.config.maxSize || 10000;
35897          var center = this.mgr.getRegion("center");
35898          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35899     },
35900
35901     onSplitMove : function(split, newSize){
35902         this.fireEvent("resized", this, newSize);
35903     },
35904     
35905     /** 
35906      * Returns the {@link Roo.SplitBar} for this region.
35907      * @return {Roo.SplitBar}
35908      */
35909     getSplitBar : function(){
35910         return this.split;
35911     },
35912     
35913     hide : function(){
35914         this.hideSplitter();
35915         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35916     },
35917
35918     hideSplitter : function(){
35919         if(this.split){
35920             this.split.el.setLocation(-2000,-2000);
35921             this.split.el.hide();
35922         }
35923     },
35924
35925     show : function(){
35926         if(this.split){
35927             this.split.el.show();
35928         }
35929         Roo.bootstrap.layout.Split.superclass.show.call(this);
35930     },
35931     
35932     beforeSlide: function(){
35933         if(Roo.isGecko){// firefox overflow auto bug workaround
35934             this.bodyEl.clip();
35935             if(this.tabs) {
35936                 this.tabs.bodyEl.clip();
35937             }
35938             if(this.activePanel){
35939                 this.activePanel.getEl().clip();
35940                 
35941                 if(this.activePanel.beforeSlide){
35942                     this.activePanel.beforeSlide();
35943                 }
35944             }
35945         }
35946     },
35947     
35948     afterSlide : function(){
35949         if(Roo.isGecko){// firefox overflow auto bug workaround
35950             this.bodyEl.unclip();
35951             if(this.tabs) {
35952                 this.tabs.bodyEl.unclip();
35953             }
35954             if(this.activePanel){
35955                 this.activePanel.getEl().unclip();
35956                 if(this.activePanel.afterSlide){
35957                     this.activePanel.afterSlide();
35958                 }
35959             }
35960         }
35961     },
35962
35963     initAutoHide : function(){
35964         if(this.autoHide !== false){
35965             if(!this.autoHideHd){
35966                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35967                 this.autoHideHd = {
35968                     "mouseout": function(e){
35969                         if(!e.within(this.el, true)){
35970                             st.delay(500);
35971                         }
35972                     },
35973                     "mouseover" : function(e){
35974                         st.cancel();
35975                     },
35976                     scope : this
35977                 };
35978             }
35979             this.el.on(this.autoHideHd);
35980         }
35981     },
35982
35983     clearAutoHide : function(){
35984         if(this.autoHide !== false){
35985             this.el.un("mouseout", this.autoHideHd.mouseout);
35986             this.el.un("mouseover", this.autoHideHd.mouseover);
35987         }
35988     },
35989
35990     clearMonitor : function(){
35991         Roo.get(document).un("click", this.slideInIf, this);
35992     },
35993
35994     // these names are backwards but not changed for compat
35995     slideOut : function(){
35996         if(this.isSlid || this.el.hasActiveFx()){
35997             return;
35998         }
35999         this.isSlid = true;
36000         if(this.collapseBtn){
36001             this.collapseBtn.hide();
36002         }
36003         this.closeBtnState = this.closeBtn.getStyle('display');
36004         this.closeBtn.hide();
36005         if(this.stickBtn){
36006             this.stickBtn.show();
36007         }
36008         this.el.show();
36009         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36010         this.beforeSlide();
36011         this.el.setStyle("z-index", 10001);
36012         this.el.slideIn(this.getSlideAnchor(), {
36013             callback: function(){
36014                 this.afterSlide();
36015                 this.initAutoHide();
36016                 Roo.get(document).on("click", this.slideInIf, this);
36017                 this.fireEvent("slideshow", this);
36018             },
36019             scope: this,
36020             block: true
36021         });
36022     },
36023
36024     afterSlideIn : function(){
36025         this.clearAutoHide();
36026         this.isSlid = false;
36027         this.clearMonitor();
36028         this.el.setStyle("z-index", "");
36029         if(this.collapseBtn){
36030             this.collapseBtn.show();
36031         }
36032         this.closeBtn.setStyle('display', this.closeBtnState);
36033         if(this.stickBtn){
36034             this.stickBtn.hide();
36035         }
36036         this.fireEvent("slidehide", this);
36037     },
36038
36039     slideIn : function(cb){
36040         if(!this.isSlid || this.el.hasActiveFx()){
36041             Roo.callback(cb);
36042             return;
36043         }
36044         this.isSlid = false;
36045         this.beforeSlide();
36046         this.el.slideOut(this.getSlideAnchor(), {
36047             callback: function(){
36048                 this.el.setLeftTop(-10000, -10000);
36049                 this.afterSlide();
36050                 this.afterSlideIn();
36051                 Roo.callback(cb);
36052             },
36053             scope: this,
36054             block: true
36055         });
36056     },
36057     
36058     slideInIf : function(e){
36059         if(!e.within(this.el)){
36060             this.slideIn();
36061         }
36062     },
36063
36064     animateCollapse : function(){
36065         this.beforeSlide();
36066         this.el.setStyle("z-index", 20000);
36067         var anchor = this.getSlideAnchor();
36068         this.el.slideOut(anchor, {
36069             callback : function(){
36070                 this.el.setStyle("z-index", "");
36071                 this.collapsedEl.slideIn(anchor, {duration:.3});
36072                 this.afterSlide();
36073                 this.el.setLocation(-10000,-10000);
36074                 this.el.hide();
36075                 this.fireEvent("collapsed", this);
36076             },
36077             scope: this,
36078             block: true
36079         });
36080     },
36081
36082     animateExpand : function(){
36083         this.beforeSlide();
36084         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36085         this.el.setStyle("z-index", 20000);
36086         this.collapsedEl.hide({
36087             duration:.1
36088         });
36089         this.el.slideIn(this.getSlideAnchor(), {
36090             callback : function(){
36091                 this.el.setStyle("z-index", "");
36092                 this.afterSlide();
36093                 if(this.split){
36094                     this.split.el.show();
36095                 }
36096                 this.fireEvent("invalidated", this);
36097                 this.fireEvent("expanded", this);
36098             },
36099             scope: this,
36100             block: true
36101         });
36102     },
36103
36104     anchors : {
36105         "west" : "left",
36106         "east" : "right",
36107         "north" : "top",
36108         "south" : "bottom"
36109     },
36110
36111     sanchors : {
36112         "west" : "l",
36113         "east" : "r",
36114         "north" : "t",
36115         "south" : "b"
36116     },
36117
36118     canchors : {
36119         "west" : "tl-tr",
36120         "east" : "tr-tl",
36121         "north" : "tl-bl",
36122         "south" : "bl-tl"
36123     },
36124
36125     getAnchor : function(){
36126         return this.anchors[this.position];
36127     },
36128
36129     getCollapseAnchor : function(){
36130         return this.canchors[this.position];
36131     },
36132
36133     getSlideAnchor : function(){
36134         return this.sanchors[this.position];
36135     },
36136
36137     getAlignAdj : function(){
36138         var cm = this.cmargins;
36139         switch(this.position){
36140             case "west":
36141                 return [0, 0];
36142             break;
36143             case "east":
36144                 return [0, 0];
36145             break;
36146             case "north":
36147                 return [0, 0];
36148             break;
36149             case "south":
36150                 return [0, 0];
36151             break;
36152         }
36153     },
36154
36155     getExpandAdj : function(){
36156         var c = this.collapsedEl, cm = this.cmargins;
36157         switch(this.position){
36158             case "west":
36159                 return [-(cm.right+c.getWidth()+cm.left), 0];
36160             break;
36161             case "east":
36162                 return [cm.right+c.getWidth()+cm.left, 0];
36163             break;
36164             case "north":
36165                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36166             break;
36167             case "south":
36168                 return [0, cm.top+cm.bottom+c.getHeight()];
36169             break;
36170         }
36171     }
36172 });/*
36173  * Based on:
36174  * Ext JS Library 1.1.1
36175  * Copyright(c) 2006-2007, Ext JS, LLC.
36176  *
36177  * Originally Released Under LGPL - original licence link has changed is not relivant.
36178  *
36179  * Fork - LGPL
36180  * <script type="text/javascript">
36181  */
36182 /*
36183  * These classes are private internal classes
36184  */
36185 Roo.bootstrap.layout.Center = function(config){
36186     config.region = "center";
36187     Roo.bootstrap.layout.Region.call(this, config);
36188     this.visible = true;
36189     this.minWidth = config.minWidth || 20;
36190     this.minHeight = config.minHeight || 20;
36191 };
36192
36193 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36194     hide : function(){
36195         // center panel can't be hidden
36196     },
36197     
36198     show : function(){
36199         // center panel can't be hidden
36200     },
36201     
36202     getMinWidth: function(){
36203         return this.minWidth;
36204     },
36205     
36206     getMinHeight: function(){
36207         return this.minHeight;
36208     }
36209 });
36210
36211
36212
36213
36214  
36215
36216
36217
36218
36219
36220 Roo.bootstrap.layout.North = function(config)
36221 {
36222     config.region = 'north';
36223     config.cursor = 'n-resize';
36224     
36225     Roo.bootstrap.layout.Split.call(this, config);
36226     
36227     
36228     if(this.split){
36229         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36230         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36231         this.split.el.addClass("roo-layout-split-v");
36232     }
36233     var size = config.initialSize || config.height;
36234     if(typeof size != "undefined"){
36235         this.el.setHeight(size);
36236     }
36237 };
36238 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36239 {
36240     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36241     
36242     
36243     
36244     getBox : function(){
36245         if(this.collapsed){
36246             return this.collapsedEl.getBox();
36247         }
36248         var box = this.el.getBox();
36249         if(this.split){
36250             box.height += this.split.el.getHeight();
36251         }
36252         return box;
36253     },
36254     
36255     updateBox : function(box){
36256         if(this.split && !this.collapsed){
36257             box.height -= this.split.el.getHeight();
36258             this.split.el.setLeft(box.x);
36259             this.split.el.setTop(box.y+box.height);
36260             this.split.el.setWidth(box.width);
36261         }
36262         if(this.collapsed){
36263             this.updateBody(box.width, null);
36264         }
36265         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36266     }
36267 });
36268
36269
36270
36271
36272
36273 Roo.bootstrap.layout.South = function(config){
36274     config.region = 'south';
36275     config.cursor = 's-resize';
36276     Roo.bootstrap.layout.Split.call(this, config);
36277     if(this.split){
36278         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36279         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36280         this.split.el.addClass("roo-layout-split-v");
36281     }
36282     var size = config.initialSize || config.height;
36283     if(typeof size != "undefined"){
36284         this.el.setHeight(size);
36285     }
36286 };
36287
36288 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36289     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36290     getBox : function(){
36291         if(this.collapsed){
36292             return this.collapsedEl.getBox();
36293         }
36294         var box = this.el.getBox();
36295         if(this.split){
36296             var sh = this.split.el.getHeight();
36297             box.height += sh;
36298             box.y -= sh;
36299         }
36300         return box;
36301     },
36302     
36303     updateBox : function(box){
36304         if(this.split && !this.collapsed){
36305             var sh = this.split.el.getHeight();
36306             box.height -= sh;
36307             box.y += sh;
36308             this.split.el.setLeft(box.x);
36309             this.split.el.setTop(box.y-sh);
36310             this.split.el.setWidth(box.width);
36311         }
36312         if(this.collapsed){
36313             this.updateBody(box.width, null);
36314         }
36315         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36316     }
36317 });
36318
36319 Roo.bootstrap.layout.East = function(config){
36320     config.region = "east";
36321     config.cursor = "e-resize";
36322     Roo.bootstrap.layout.Split.call(this, config);
36323     if(this.split){
36324         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36325         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36326         this.split.el.addClass("roo-layout-split-h");
36327     }
36328     var size = config.initialSize || config.width;
36329     if(typeof size != "undefined"){
36330         this.el.setWidth(size);
36331     }
36332 };
36333 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36334     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36335     getBox : function(){
36336         if(this.collapsed){
36337             return this.collapsedEl.getBox();
36338         }
36339         var box = this.el.getBox();
36340         if(this.split){
36341             var sw = this.split.el.getWidth();
36342             box.width += sw;
36343             box.x -= sw;
36344         }
36345         return box;
36346     },
36347
36348     updateBox : function(box){
36349         if(this.split && !this.collapsed){
36350             var sw = this.split.el.getWidth();
36351             box.width -= sw;
36352             this.split.el.setLeft(box.x);
36353             this.split.el.setTop(box.y);
36354             this.split.el.setHeight(box.height);
36355             box.x += sw;
36356         }
36357         if(this.collapsed){
36358             this.updateBody(null, box.height);
36359         }
36360         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36361     }
36362 });
36363
36364 Roo.bootstrap.layout.West = function(config){
36365     config.region = "west";
36366     config.cursor = "w-resize";
36367     
36368     Roo.bootstrap.layout.Split.call(this, config);
36369     if(this.split){
36370         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36371         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36372         this.split.el.addClass("roo-layout-split-h");
36373     }
36374     
36375 };
36376 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36377     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36378     
36379     onRender: function(ctr, pos)
36380     {
36381         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36382         var size = this.config.initialSize || this.config.width;
36383         if(typeof size != "undefined"){
36384             this.el.setWidth(size);
36385         }
36386     },
36387     
36388     getBox : function(){
36389         if(this.collapsed){
36390             return this.collapsedEl.getBox();
36391         }
36392         var box = this.el.getBox();
36393         if(this.split){
36394             box.width += this.split.el.getWidth();
36395         }
36396         return box;
36397     },
36398     
36399     updateBox : function(box){
36400         if(this.split && !this.collapsed){
36401             var sw = this.split.el.getWidth();
36402             box.width -= sw;
36403             this.split.el.setLeft(box.x+box.width);
36404             this.split.el.setTop(box.y);
36405             this.split.el.setHeight(box.height);
36406         }
36407         if(this.collapsed){
36408             this.updateBody(null, box.height);
36409         }
36410         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36411     }
36412 });
36413 Roo.namespace("Roo.bootstrap.panel");/*
36414  * Based on:
36415  * Ext JS Library 1.1.1
36416  * Copyright(c) 2006-2007, Ext JS, LLC.
36417  *
36418  * Originally Released Under LGPL - original licence link has changed is not relivant.
36419  *
36420  * Fork - LGPL
36421  * <script type="text/javascript">
36422  */
36423 /**
36424  * @class Roo.ContentPanel
36425  * @extends Roo.util.Observable
36426  * A basic ContentPanel element.
36427  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36428  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36429  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36430  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36431  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36432  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36433  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36434  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36435  * @cfg {String} title          The title for this panel
36436  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36437  * @cfg {String} url            Calls {@link #setUrl} with this value
36438  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36439  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36440  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36441  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36442  * @cfg {Boolean} badges render the badges
36443
36444  * @constructor
36445  * Create a new ContentPanel.
36446  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36447  * @param {String/Object} config A string to set only the title or a config object
36448  * @param {String} content (optional) Set the HTML content for this panel
36449  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36450  */
36451 Roo.bootstrap.panel.Content = function( config){
36452     
36453     this.tpl = config.tpl || false;
36454     
36455     var el = config.el;
36456     var content = config.content;
36457
36458     if(config.autoCreate){ // xtype is available if this is called from factory
36459         el = Roo.id();
36460     }
36461     this.el = Roo.get(el);
36462     if(!this.el && config && config.autoCreate){
36463         if(typeof config.autoCreate == "object"){
36464             if(!config.autoCreate.id){
36465                 config.autoCreate.id = config.id||el;
36466             }
36467             this.el = Roo.DomHelper.append(document.body,
36468                         config.autoCreate, true);
36469         }else{
36470             var elcfg =  {   tag: "div",
36471                             cls: "roo-layout-inactive-content",
36472                             id: config.id||el
36473                             };
36474             if (config.html) {
36475                 elcfg.html = config.html;
36476                 
36477             }
36478                         
36479             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36480         }
36481     } 
36482     this.closable = false;
36483     this.loaded = false;
36484     this.active = false;
36485    
36486       
36487     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36488         
36489         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36490         
36491         this.wrapEl = this.el; //this.el.wrap();
36492         var ti = [];
36493         if (config.toolbar.items) {
36494             ti = config.toolbar.items ;
36495             delete config.toolbar.items ;
36496         }
36497         
36498         var nitems = [];
36499         this.toolbar.render(this.wrapEl, 'before');
36500         for(var i =0;i < ti.length;i++) {
36501           //  Roo.log(['add child', items[i]]);
36502             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36503         }
36504         this.toolbar.items = nitems;
36505         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36506         delete config.toolbar;
36507         
36508     }
36509     /*
36510     // xtype created footer. - not sure if will work as we normally have to render first..
36511     if (this.footer && !this.footer.el && this.footer.xtype) {
36512         if (!this.wrapEl) {
36513             this.wrapEl = this.el.wrap();
36514         }
36515     
36516         this.footer.container = this.wrapEl.createChild();
36517          
36518         this.footer = Roo.factory(this.footer, Roo);
36519         
36520     }
36521     */
36522     
36523      if(typeof config == "string"){
36524         this.title = config;
36525     }else{
36526         Roo.apply(this, config);
36527     }
36528     
36529     if(this.resizeEl){
36530         this.resizeEl = Roo.get(this.resizeEl, true);
36531     }else{
36532         this.resizeEl = this.el;
36533     }
36534     // handle view.xtype
36535     
36536  
36537     
36538     
36539     this.addEvents({
36540         /**
36541          * @event activate
36542          * Fires when this panel is activated. 
36543          * @param {Roo.ContentPanel} this
36544          */
36545         "activate" : true,
36546         /**
36547          * @event deactivate
36548          * Fires when this panel is activated. 
36549          * @param {Roo.ContentPanel} this
36550          */
36551         "deactivate" : true,
36552
36553         /**
36554          * @event resize
36555          * Fires when this panel is resized if fitToFrame is true.
36556          * @param {Roo.ContentPanel} this
36557          * @param {Number} width The width after any component adjustments
36558          * @param {Number} height The height after any component adjustments
36559          */
36560         "resize" : true,
36561         
36562          /**
36563          * @event render
36564          * Fires when this tab is created
36565          * @param {Roo.ContentPanel} this
36566          */
36567         "render" : true
36568         
36569         
36570         
36571     });
36572     
36573
36574     
36575     
36576     if(this.autoScroll){
36577         this.resizeEl.setStyle("overflow", "auto");
36578     } else {
36579         // fix randome scrolling
36580         //this.el.on('scroll', function() {
36581         //    Roo.log('fix random scolling');
36582         //    this.scrollTo('top',0); 
36583         //});
36584     }
36585     content = content || this.content;
36586     if(content){
36587         this.setContent(content);
36588     }
36589     if(config && config.url){
36590         this.setUrl(this.url, this.params, this.loadOnce);
36591     }
36592     
36593     
36594     
36595     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36596     
36597     if (this.view && typeof(this.view.xtype) != 'undefined') {
36598         this.view.el = this.el.appendChild(document.createElement("div"));
36599         this.view = Roo.factory(this.view); 
36600         this.view.render  &&  this.view.render(false, '');  
36601     }
36602     
36603     
36604     this.fireEvent('render', this);
36605 };
36606
36607 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36608     
36609     tabTip : '',
36610     
36611     setRegion : function(region){
36612         this.region = region;
36613         this.setActiveClass(region && !this.background);
36614     },
36615     
36616     
36617     setActiveClass: function(state)
36618     {
36619         if(state){
36620            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36621            this.el.setStyle('position','relative');
36622         }else{
36623            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36624            this.el.setStyle('position', 'absolute');
36625         } 
36626     },
36627     
36628     /**
36629      * Returns the toolbar for this Panel if one was configured. 
36630      * @return {Roo.Toolbar} 
36631      */
36632     getToolbar : function(){
36633         return this.toolbar;
36634     },
36635     
36636     setActiveState : function(active)
36637     {
36638         this.active = active;
36639         this.setActiveClass(active);
36640         if(!active){
36641             if(this.fireEvent("deactivate", this) === false){
36642                 return false;
36643             }
36644             return true;
36645         }
36646         this.fireEvent("activate", this);
36647         return true;
36648     },
36649     /**
36650      * Updates this panel's element
36651      * @param {String} content The new content
36652      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36653     */
36654     setContent : function(content, loadScripts){
36655         this.el.update(content, loadScripts);
36656     },
36657
36658     ignoreResize : function(w, h){
36659         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36660             return true;
36661         }else{
36662             this.lastSize = {width: w, height: h};
36663             return false;
36664         }
36665     },
36666     /**
36667      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36668      * @return {Roo.UpdateManager} The UpdateManager
36669      */
36670     getUpdateManager : function(){
36671         return this.el.getUpdateManager();
36672     },
36673      /**
36674      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36675      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36676 <pre><code>
36677 panel.load({
36678     url: "your-url.php",
36679     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36680     callback: yourFunction,
36681     scope: yourObject, //(optional scope)
36682     discardUrl: false,
36683     nocache: false,
36684     text: "Loading...",
36685     timeout: 30,
36686     scripts: false
36687 });
36688 </code></pre>
36689      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36690      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
36691      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
36692      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36693      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
36694      * @return {Roo.ContentPanel} this
36695      */
36696     load : function(){
36697         var um = this.el.getUpdateManager();
36698         um.update.apply(um, arguments);
36699         return this;
36700     },
36701
36702
36703     /**
36704      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
36705      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36706      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
36707      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
36708      * @return {Roo.UpdateManager} The UpdateManager
36709      */
36710     setUrl : function(url, params, loadOnce){
36711         if(this.refreshDelegate){
36712             this.removeListener("activate", this.refreshDelegate);
36713         }
36714         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36715         this.on("activate", this.refreshDelegate);
36716         return this.el.getUpdateManager();
36717     },
36718     
36719     _handleRefresh : function(url, params, loadOnce){
36720         if(!loadOnce || !this.loaded){
36721             var updater = this.el.getUpdateManager();
36722             updater.update(url, params, this._setLoaded.createDelegate(this));
36723         }
36724     },
36725     
36726     _setLoaded : function(){
36727         this.loaded = true;
36728     }, 
36729     
36730     /**
36731      * Returns this panel's id
36732      * @return {String} 
36733      */
36734     getId : function(){
36735         return this.el.id;
36736     },
36737     
36738     /** 
36739      * Returns this panel's element - used by regiosn to add.
36740      * @return {Roo.Element} 
36741      */
36742     getEl : function(){
36743         return this.wrapEl || this.el;
36744     },
36745     
36746    
36747     
36748     adjustForComponents : function(width, height)
36749     {
36750         //Roo.log('adjustForComponents ');
36751         if(this.resizeEl != this.el){
36752             width -= this.el.getFrameWidth('lr');
36753             height -= this.el.getFrameWidth('tb');
36754         }
36755         if(this.toolbar){
36756             var te = this.toolbar.getEl();
36757             te.setWidth(width);
36758             height -= te.getHeight();
36759         }
36760         if(this.footer){
36761             var te = this.footer.getEl();
36762             te.setWidth(width);
36763             height -= te.getHeight();
36764         }
36765         
36766         
36767         if(this.adjustments){
36768             width += this.adjustments[0];
36769             height += this.adjustments[1];
36770         }
36771         return {"width": width, "height": height};
36772     },
36773     
36774     setSize : function(width, height){
36775         if(this.fitToFrame && !this.ignoreResize(width, height)){
36776             if(this.fitContainer && this.resizeEl != this.el){
36777                 this.el.setSize(width, height);
36778             }
36779             var size = this.adjustForComponents(width, height);
36780             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36781             this.fireEvent('resize', this, size.width, size.height);
36782         }
36783     },
36784     
36785     /**
36786      * Returns this panel's title
36787      * @return {String} 
36788      */
36789     getTitle : function(){
36790         
36791         if (typeof(this.title) != 'object') {
36792             return this.title;
36793         }
36794         
36795         var t = '';
36796         for (var k in this.title) {
36797             if (!this.title.hasOwnProperty(k)) {
36798                 continue;
36799             }
36800             
36801             if (k.indexOf('-') >= 0) {
36802                 var s = k.split('-');
36803                 for (var i = 0; i<s.length; i++) {
36804                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36805                 }
36806             } else {
36807                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36808             }
36809         }
36810         return t;
36811     },
36812     
36813     /**
36814      * Set this panel's title
36815      * @param {String} title
36816      */
36817     setTitle : function(title){
36818         this.title = title;
36819         if(this.region){
36820             this.region.updatePanelTitle(this, title);
36821         }
36822     },
36823     
36824     /**
36825      * Returns true is this panel was configured to be closable
36826      * @return {Boolean} 
36827      */
36828     isClosable : function(){
36829         return this.closable;
36830     },
36831     
36832     beforeSlide : function(){
36833         this.el.clip();
36834         this.resizeEl.clip();
36835     },
36836     
36837     afterSlide : function(){
36838         this.el.unclip();
36839         this.resizeEl.unclip();
36840     },
36841     
36842     /**
36843      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36844      *   Will fail silently if the {@link #setUrl} method has not been called.
36845      *   This does not activate the panel, just updates its content.
36846      */
36847     refresh : function(){
36848         if(this.refreshDelegate){
36849            this.loaded = false;
36850            this.refreshDelegate();
36851         }
36852     },
36853     
36854     /**
36855      * Destroys this panel
36856      */
36857     destroy : function(){
36858         this.el.removeAllListeners();
36859         var tempEl = document.createElement("span");
36860         tempEl.appendChild(this.el.dom);
36861         tempEl.innerHTML = "";
36862         this.el.remove();
36863         this.el = null;
36864     },
36865     
36866     /**
36867      * form - if the content panel contains a form - this is a reference to it.
36868      * @type {Roo.form.Form}
36869      */
36870     form : false,
36871     /**
36872      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36873      *    This contains a reference to it.
36874      * @type {Roo.View}
36875      */
36876     view : false,
36877     
36878       /**
36879      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36880      * <pre><code>
36881
36882 layout.addxtype({
36883        xtype : 'Form',
36884        items: [ .... ]
36885    }
36886 );
36887
36888 </code></pre>
36889      * @param {Object} cfg Xtype definition of item to add.
36890      */
36891     
36892     
36893     getChildContainer: function () {
36894         return this.getEl();
36895     }
36896     
36897     
36898     /*
36899         var  ret = new Roo.factory(cfg);
36900         return ret;
36901         
36902         
36903         // add form..
36904         if (cfg.xtype.match(/^Form$/)) {
36905             
36906             var el;
36907             //if (this.footer) {
36908             //    el = this.footer.container.insertSibling(false, 'before');
36909             //} else {
36910                 el = this.el.createChild();
36911             //}
36912
36913             this.form = new  Roo.form.Form(cfg);
36914             
36915             
36916             if ( this.form.allItems.length) {
36917                 this.form.render(el.dom);
36918             }
36919             return this.form;
36920         }
36921         // should only have one of theses..
36922         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36923             // views.. should not be just added - used named prop 'view''
36924             
36925             cfg.el = this.el.appendChild(document.createElement("div"));
36926             // factory?
36927             
36928             var ret = new Roo.factory(cfg);
36929              
36930              ret.render && ret.render(false, ''); // render blank..
36931             this.view = ret;
36932             return ret;
36933         }
36934         return false;
36935     }
36936     \*/
36937 });
36938  
36939 /**
36940  * @class Roo.bootstrap.panel.Grid
36941  * @extends Roo.bootstrap.panel.Content
36942  * @constructor
36943  * Create a new GridPanel.
36944  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36945  * @param {Object} config A the config object
36946   
36947  */
36948
36949
36950
36951 Roo.bootstrap.panel.Grid = function(config)
36952 {
36953     
36954       
36955     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36956         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36957
36958     config.el = this.wrapper;
36959     //this.el = this.wrapper;
36960     
36961       if (config.container) {
36962         // ctor'ed from a Border/panel.grid
36963         
36964         
36965         this.wrapper.setStyle("overflow", "hidden");
36966         this.wrapper.addClass('roo-grid-container');
36967
36968     }
36969     
36970     
36971     if(config.toolbar){
36972         var tool_el = this.wrapper.createChild();    
36973         this.toolbar = Roo.factory(config.toolbar);
36974         var ti = [];
36975         if (config.toolbar.items) {
36976             ti = config.toolbar.items ;
36977             delete config.toolbar.items ;
36978         }
36979         
36980         var nitems = [];
36981         this.toolbar.render(tool_el);
36982         for(var i =0;i < ti.length;i++) {
36983           //  Roo.log(['add child', items[i]]);
36984             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36985         }
36986         this.toolbar.items = nitems;
36987         
36988         delete config.toolbar;
36989     }
36990     
36991     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36992     config.grid.scrollBody = true;;
36993     config.grid.monitorWindowResize = false; // turn off autosizing
36994     config.grid.autoHeight = false;
36995     config.grid.autoWidth = false;
36996     
36997     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36998     
36999     if (config.background) {
37000         // render grid on panel activation (if panel background)
37001         this.on('activate', function(gp) {
37002             if (!gp.grid.rendered) {
37003                 gp.grid.render(this.wrapper);
37004                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37005             }
37006         });
37007             
37008     } else {
37009         this.grid.render(this.wrapper);
37010         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37011
37012     }
37013     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37014     // ??? needed ??? config.el = this.wrapper;
37015     
37016     
37017     
37018   
37019     // xtype created footer. - not sure if will work as we normally have to render first..
37020     if (this.footer && !this.footer.el && this.footer.xtype) {
37021         
37022         var ctr = this.grid.getView().getFooterPanel(true);
37023         this.footer.dataSource = this.grid.dataSource;
37024         this.footer = Roo.factory(this.footer, Roo);
37025         this.footer.render(ctr);
37026         
37027     }
37028     
37029     
37030     
37031     
37032      
37033 };
37034
37035 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37036     getId : function(){
37037         return this.grid.id;
37038     },
37039     
37040     /**
37041      * Returns the grid for this panel
37042      * @return {Roo.bootstrap.Table} 
37043      */
37044     getGrid : function(){
37045         return this.grid;    
37046     },
37047     
37048     setSize : function(width, height){
37049         if(!this.ignoreResize(width, height)){
37050             var grid = this.grid;
37051             var size = this.adjustForComponents(width, height);
37052             var gridel = grid.getGridEl();
37053             gridel.setSize(size.width, size.height);
37054             /*
37055             var thd = grid.getGridEl().select('thead',true).first();
37056             var tbd = grid.getGridEl().select('tbody', true).first();
37057             if (tbd) {
37058                 tbd.setSize(width, height - thd.getHeight());
37059             }
37060             */
37061             grid.autoSize();
37062         }
37063     },
37064      
37065     
37066     
37067     beforeSlide : function(){
37068         this.grid.getView().scroller.clip();
37069     },
37070     
37071     afterSlide : function(){
37072         this.grid.getView().scroller.unclip();
37073     },
37074     
37075     destroy : function(){
37076         this.grid.destroy();
37077         delete this.grid;
37078         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37079     }
37080 });
37081
37082 /**
37083  * @class Roo.bootstrap.panel.Nest
37084  * @extends Roo.bootstrap.panel.Content
37085  * @constructor
37086  * Create a new Panel, that can contain a layout.Border.
37087  * 
37088  * 
37089  * @param {Roo.BorderLayout} layout The layout for this panel
37090  * @param {String/Object} config A string to set only the title or a config object
37091  */
37092 Roo.bootstrap.panel.Nest = function(config)
37093 {
37094     // construct with only one argument..
37095     /* FIXME - implement nicer consturctors
37096     if (layout.layout) {
37097         config = layout;
37098         layout = config.layout;
37099         delete config.layout;
37100     }
37101     if (layout.xtype && !layout.getEl) {
37102         // then layout needs constructing..
37103         layout = Roo.factory(layout, Roo);
37104     }
37105     */
37106     
37107     config.el =  config.layout.getEl();
37108     
37109     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37110     
37111     config.layout.monitorWindowResize = false; // turn off autosizing
37112     this.layout = config.layout;
37113     this.layout.getEl().addClass("roo-layout-nested-layout");
37114     
37115     
37116     
37117     
37118 };
37119
37120 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37121
37122     setSize : function(width, height){
37123         if(!this.ignoreResize(width, height)){
37124             var size = this.adjustForComponents(width, height);
37125             var el = this.layout.getEl();
37126             if (size.height < 1) {
37127                 el.setWidth(size.width);   
37128             } else {
37129                 el.setSize(size.width, size.height);
37130             }
37131             var touch = el.dom.offsetWidth;
37132             this.layout.layout();
37133             // ie requires a double layout on the first pass
37134             if(Roo.isIE && !this.initialized){
37135                 this.initialized = true;
37136                 this.layout.layout();
37137             }
37138         }
37139     },
37140     
37141     // activate all subpanels if not currently active..
37142     
37143     setActiveState : function(active){
37144         this.active = active;
37145         this.setActiveClass(active);
37146         
37147         if(!active){
37148             this.fireEvent("deactivate", this);
37149             return;
37150         }
37151         
37152         this.fireEvent("activate", this);
37153         // not sure if this should happen before or after..
37154         if (!this.layout) {
37155             return; // should not happen..
37156         }
37157         var reg = false;
37158         for (var r in this.layout.regions) {
37159             reg = this.layout.getRegion(r);
37160             if (reg.getActivePanel()) {
37161                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37162                 reg.setActivePanel(reg.getActivePanel());
37163                 continue;
37164             }
37165             if (!reg.panels.length) {
37166                 continue;
37167             }
37168             reg.showPanel(reg.getPanel(0));
37169         }
37170         
37171         
37172         
37173         
37174     },
37175     
37176     /**
37177      * Returns the nested BorderLayout for this panel
37178      * @return {Roo.BorderLayout} 
37179      */
37180     getLayout : function(){
37181         return this.layout;
37182     },
37183     
37184      /**
37185      * Adds a xtype elements to the layout of the nested panel
37186      * <pre><code>
37187
37188 panel.addxtype({
37189        xtype : 'ContentPanel',
37190        region: 'west',
37191        items: [ .... ]
37192    }
37193 );
37194
37195 panel.addxtype({
37196         xtype : 'NestedLayoutPanel',
37197         region: 'west',
37198         layout: {
37199            center: { },
37200            west: { }   
37201         },
37202         items : [ ... list of content panels or nested layout panels.. ]
37203    }
37204 );
37205 </code></pre>
37206      * @param {Object} cfg Xtype definition of item to add.
37207      */
37208     addxtype : function(cfg) {
37209         return this.layout.addxtype(cfg);
37210     
37211     }
37212 });        /*
37213  * Based on:
37214  * Ext JS Library 1.1.1
37215  * Copyright(c) 2006-2007, Ext JS, LLC.
37216  *
37217  * Originally Released Under LGPL - original licence link has changed is not relivant.
37218  *
37219  * Fork - LGPL
37220  * <script type="text/javascript">
37221  */
37222 /**
37223  * @class Roo.TabPanel
37224  * @extends Roo.util.Observable
37225  * A lightweight tab container.
37226  * <br><br>
37227  * Usage:
37228  * <pre><code>
37229 // basic tabs 1, built from existing content
37230 var tabs = new Roo.TabPanel("tabs1");
37231 tabs.addTab("script", "View Script");
37232 tabs.addTab("markup", "View Markup");
37233 tabs.activate("script");
37234
37235 // more advanced tabs, built from javascript
37236 var jtabs = new Roo.TabPanel("jtabs");
37237 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37238
37239 // set up the UpdateManager
37240 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37241 var updater = tab2.getUpdateManager();
37242 updater.setDefaultUrl("ajax1.htm");
37243 tab2.on('activate', updater.refresh, updater, true);
37244
37245 // Use setUrl for Ajax loading
37246 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37247 tab3.setUrl("ajax2.htm", null, true);
37248
37249 // Disabled tab
37250 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37251 tab4.disable();
37252
37253 jtabs.activate("jtabs-1");
37254  * </code></pre>
37255  * @constructor
37256  * Create a new TabPanel.
37257  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37258  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37259  */
37260 Roo.bootstrap.panel.Tabs = function(config){
37261     /**
37262     * The container element for this TabPanel.
37263     * @type Roo.Element
37264     */
37265     this.el = Roo.get(config.el);
37266     delete config.el;
37267     if(config){
37268         if(typeof config == "boolean"){
37269             this.tabPosition = config ? "bottom" : "top";
37270         }else{
37271             Roo.apply(this, config);
37272         }
37273     }
37274     
37275     if(this.tabPosition == "bottom"){
37276         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37277         this.el.addClass("roo-tabs-bottom");
37278     }
37279     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37280     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37281     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37282     if(Roo.isIE){
37283         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37284     }
37285     if(this.tabPosition != "bottom"){
37286         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37287          * @type Roo.Element
37288          */
37289         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37290         this.el.addClass("roo-tabs-top");
37291     }
37292     this.items = [];
37293
37294     this.bodyEl.setStyle("position", "relative");
37295
37296     this.active = null;
37297     this.activateDelegate = this.activate.createDelegate(this);
37298
37299     this.addEvents({
37300         /**
37301          * @event tabchange
37302          * Fires when the active tab changes
37303          * @param {Roo.TabPanel} this
37304          * @param {Roo.TabPanelItem} activePanel The new active tab
37305          */
37306         "tabchange": true,
37307         /**
37308          * @event beforetabchange
37309          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37310          * @param {Roo.TabPanel} this
37311          * @param {Object} e Set cancel to true on this object to cancel the tab change
37312          * @param {Roo.TabPanelItem} tab The tab being changed to
37313          */
37314         "beforetabchange" : true
37315     });
37316
37317     Roo.EventManager.onWindowResize(this.onResize, this);
37318     this.cpad = this.el.getPadding("lr");
37319     this.hiddenCount = 0;
37320
37321
37322     // toolbar on the tabbar support...
37323     if (this.toolbar) {
37324         alert("no toolbar support yet");
37325         this.toolbar  = false;
37326         /*
37327         var tcfg = this.toolbar;
37328         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37329         this.toolbar = new Roo.Toolbar(tcfg);
37330         if (Roo.isSafari) {
37331             var tbl = tcfg.container.child('table', true);
37332             tbl.setAttribute('width', '100%');
37333         }
37334         */
37335         
37336     }
37337    
37338
37339
37340     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37341 };
37342
37343 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37344     /*
37345      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37346      */
37347     tabPosition : "top",
37348     /*
37349      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37350      */
37351     currentTabWidth : 0,
37352     /*
37353      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37354      */
37355     minTabWidth : 40,
37356     /*
37357      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37358      */
37359     maxTabWidth : 250,
37360     /*
37361      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37362      */
37363     preferredTabWidth : 175,
37364     /*
37365      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37366      */
37367     resizeTabs : false,
37368     /*
37369      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37370      */
37371     monitorResize : true,
37372     /*
37373      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37374      */
37375     toolbar : false,
37376
37377     /**
37378      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37379      * @param {String} id The id of the div to use <b>or create</b>
37380      * @param {String} text The text for the tab
37381      * @param {String} content (optional) Content to put in the TabPanelItem body
37382      * @param {Boolean} closable (optional) True to create a close icon on the tab
37383      * @return {Roo.TabPanelItem} The created TabPanelItem
37384      */
37385     addTab : function(id, text, content, closable, tpl)
37386     {
37387         var item = new Roo.bootstrap.panel.TabItem({
37388             panel: this,
37389             id : id,
37390             text : text,
37391             closable : closable,
37392             tpl : tpl
37393         });
37394         this.addTabItem(item);
37395         if(content){
37396             item.setContent(content);
37397         }
37398         return item;
37399     },
37400
37401     /**
37402      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37403      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37404      * @return {Roo.TabPanelItem}
37405      */
37406     getTab : function(id){
37407         return this.items[id];
37408     },
37409
37410     /**
37411      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37412      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37413      */
37414     hideTab : function(id){
37415         var t = this.items[id];
37416         if(!t.isHidden()){
37417            t.setHidden(true);
37418            this.hiddenCount++;
37419            this.autoSizeTabs();
37420         }
37421     },
37422
37423     /**
37424      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37425      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37426      */
37427     unhideTab : function(id){
37428         var t = this.items[id];
37429         if(t.isHidden()){
37430            t.setHidden(false);
37431            this.hiddenCount--;
37432            this.autoSizeTabs();
37433         }
37434     },
37435
37436     /**
37437      * Adds an existing {@link Roo.TabPanelItem}.
37438      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37439      */
37440     addTabItem : function(item){
37441         this.items[item.id] = item;
37442         this.items.push(item);
37443       //  if(this.resizeTabs){
37444     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37445   //         this.autoSizeTabs();
37446 //        }else{
37447 //            item.autoSize();
37448        // }
37449     },
37450
37451     /**
37452      * Removes a {@link Roo.TabPanelItem}.
37453      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37454      */
37455     removeTab : function(id){
37456         var items = this.items;
37457         var tab = items[id];
37458         if(!tab) { return; }
37459         var index = items.indexOf(tab);
37460         if(this.active == tab && items.length > 1){
37461             var newTab = this.getNextAvailable(index);
37462             if(newTab) {
37463                 newTab.activate();
37464             }
37465         }
37466         this.stripEl.dom.removeChild(tab.pnode.dom);
37467         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37468             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37469         }
37470         items.splice(index, 1);
37471         delete this.items[tab.id];
37472         tab.fireEvent("close", tab);
37473         tab.purgeListeners();
37474         this.autoSizeTabs();
37475     },
37476
37477     getNextAvailable : function(start){
37478         var items = this.items;
37479         var index = start;
37480         // look for a next tab that will slide over to
37481         // replace the one being removed
37482         while(index < items.length){
37483             var item = items[++index];
37484             if(item && !item.isHidden()){
37485                 return item;
37486             }
37487         }
37488         // if one isn't found select the previous tab (on the left)
37489         index = start;
37490         while(index >= 0){
37491             var item = items[--index];
37492             if(item && !item.isHidden()){
37493                 return item;
37494             }
37495         }
37496         return null;
37497     },
37498
37499     /**
37500      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37501      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37502      */
37503     disableTab : function(id){
37504         var tab = this.items[id];
37505         if(tab && this.active != tab){
37506             tab.disable();
37507         }
37508     },
37509
37510     /**
37511      * Enables a {@link Roo.TabPanelItem} that is disabled.
37512      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37513      */
37514     enableTab : function(id){
37515         var tab = this.items[id];
37516         tab.enable();
37517     },
37518
37519     /**
37520      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37521      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37522      * @return {Roo.TabPanelItem} The TabPanelItem.
37523      */
37524     activate : function(id){
37525         var tab = this.items[id];
37526         if(!tab){
37527             return null;
37528         }
37529         if(tab == this.active || tab.disabled){
37530             return tab;
37531         }
37532         var e = {};
37533         this.fireEvent("beforetabchange", this, e, tab);
37534         if(e.cancel !== true && !tab.disabled){
37535             if(this.active){
37536                 this.active.hide();
37537             }
37538             this.active = this.items[id];
37539             this.active.show();
37540             this.fireEvent("tabchange", this, this.active);
37541         }
37542         return tab;
37543     },
37544
37545     /**
37546      * Gets the active {@link Roo.TabPanelItem}.
37547      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37548      */
37549     getActiveTab : function(){
37550         return this.active;
37551     },
37552
37553     /**
37554      * Updates the tab body element to fit the height of the container element
37555      * for overflow scrolling
37556      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37557      */
37558     syncHeight : function(targetHeight){
37559         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37560         var bm = this.bodyEl.getMargins();
37561         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37562         this.bodyEl.setHeight(newHeight);
37563         return newHeight;
37564     },
37565
37566     onResize : function(){
37567         if(this.monitorResize){
37568             this.autoSizeTabs();
37569         }
37570     },
37571
37572     /**
37573      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37574      */
37575     beginUpdate : function(){
37576         this.updating = true;
37577     },
37578
37579     /**
37580      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37581      */
37582     endUpdate : function(){
37583         this.updating = false;
37584         this.autoSizeTabs();
37585     },
37586
37587     /**
37588      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37589      */
37590     autoSizeTabs : function(){
37591         var count = this.items.length;
37592         var vcount = count - this.hiddenCount;
37593         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37594             return;
37595         }
37596         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37597         var availWidth = Math.floor(w / vcount);
37598         var b = this.stripBody;
37599         if(b.getWidth() > w){
37600             var tabs = this.items;
37601             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37602             if(availWidth < this.minTabWidth){
37603                 /*if(!this.sleft){    // incomplete scrolling code
37604                     this.createScrollButtons();
37605                 }
37606                 this.showScroll();
37607                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37608             }
37609         }else{
37610             if(this.currentTabWidth < this.preferredTabWidth){
37611                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37612             }
37613         }
37614     },
37615
37616     /**
37617      * Returns the number of tabs in this TabPanel.
37618      * @return {Number}
37619      */
37620      getCount : function(){
37621          return this.items.length;
37622      },
37623
37624     /**
37625      * Resizes all the tabs to the passed width
37626      * @param {Number} The new width
37627      */
37628     setTabWidth : function(width){
37629         this.currentTabWidth = width;
37630         for(var i = 0, len = this.items.length; i < len; i++) {
37631                 if(!this.items[i].isHidden()) {
37632                 this.items[i].setWidth(width);
37633             }
37634         }
37635     },
37636
37637     /**
37638      * Destroys this TabPanel
37639      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37640      */
37641     destroy : function(removeEl){
37642         Roo.EventManager.removeResizeListener(this.onResize, this);
37643         for(var i = 0, len = this.items.length; i < len; i++){
37644             this.items[i].purgeListeners();
37645         }
37646         if(removeEl === true){
37647             this.el.update("");
37648             this.el.remove();
37649         }
37650     },
37651     
37652     createStrip : function(container)
37653     {
37654         var strip = document.createElement("nav");
37655         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37656         container.appendChild(strip);
37657         return strip;
37658     },
37659     
37660     createStripList : function(strip)
37661     {
37662         // div wrapper for retard IE
37663         // returns the "tr" element.
37664         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37665         //'<div class="x-tabs-strip-wrap">'+
37666           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37667           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37668         return strip.firstChild; //.firstChild.firstChild.firstChild;
37669     },
37670     createBody : function(container)
37671     {
37672         var body = document.createElement("div");
37673         Roo.id(body, "tab-body");
37674         //Roo.fly(body).addClass("x-tabs-body");
37675         Roo.fly(body).addClass("tab-content");
37676         container.appendChild(body);
37677         return body;
37678     },
37679     createItemBody :function(bodyEl, id){
37680         var body = Roo.getDom(id);
37681         if(!body){
37682             body = document.createElement("div");
37683             body.id = id;
37684         }
37685         //Roo.fly(body).addClass("x-tabs-item-body");
37686         Roo.fly(body).addClass("tab-pane");
37687          bodyEl.insertBefore(body, bodyEl.firstChild);
37688         return body;
37689     },
37690     /** @private */
37691     createStripElements :  function(stripEl, text, closable, tpl)
37692     {
37693         var td = document.createElement("li"); // was td..
37694         
37695         
37696         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37697         
37698         
37699         stripEl.appendChild(td);
37700         /*if(closable){
37701             td.className = "x-tabs-closable";
37702             if(!this.closeTpl){
37703                 this.closeTpl = new Roo.Template(
37704                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37705                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37706                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37707                 );
37708             }
37709             var el = this.closeTpl.overwrite(td, {"text": text});
37710             var close = el.getElementsByTagName("div")[0];
37711             var inner = el.getElementsByTagName("em")[0];
37712             return {"el": el, "close": close, "inner": inner};
37713         } else {
37714         */
37715         // not sure what this is..
37716 //            if(!this.tabTpl){
37717                 //this.tabTpl = new Roo.Template(
37718                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37719                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37720                 //);
37721 //                this.tabTpl = new Roo.Template(
37722 //                   '<a href="#">' +
37723 //                   '<span unselectable="on"' +
37724 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37725 //                            ' >{text}</span></a>'
37726 //                );
37727 //                
37728 //            }
37729
37730
37731             var template = tpl || this.tabTpl || false;
37732             
37733             if(!template){
37734                 
37735                 template = new Roo.Template(
37736                    '<a href="#">' +
37737                    '<span unselectable="on"' +
37738                             (this.disableTooltips ? '' : ' title="{text}"') +
37739                             ' >{text}</span></a>'
37740                 );
37741             }
37742             
37743             switch (typeof(template)) {
37744                 case 'object' :
37745                     break;
37746                 case 'string' :
37747                     template = new Roo.Template(template);
37748                     break;
37749                 default :
37750                     break;
37751             }
37752             
37753             var el = template.overwrite(td, {"text": text});
37754             
37755             var inner = el.getElementsByTagName("span")[0];
37756             
37757             return {"el": el, "inner": inner};
37758             
37759     }
37760         
37761     
37762 });
37763
37764 /**
37765  * @class Roo.TabPanelItem
37766  * @extends Roo.util.Observable
37767  * Represents an individual item (tab plus body) in a TabPanel.
37768  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37769  * @param {String} id The id of this TabPanelItem
37770  * @param {String} text The text for the tab of this TabPanelItem
37771  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37772  */
37773 Roo.bootstrap.panel.TabItem = function(config){
37774     /**
37775      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37776      * @type Roo.TabPanel
37777      */
37778     this.tabPanel = config.panel;
37779     /**
37780      * The id for this TabPanelItem
37781      * @type String
37782      */
37783     this.id = config.id;
37784     /** @private */
37785     this.disabled = false;
37786     /** @private */
37787     this.text = config.text;
37788     /** @private */
37789     this.loaded = false;
37790     this.closable = config.closable;
37791
37792     /**
37793      * The body element for this TabPanelItem.
37794      * @type Roo.Element
37795      */
37796     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37797     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37798     this.bodyEl.setStyle("display", "block");
37799     this.bodyEl.setStyle("zoom", "1");
37800     //this.hideAction();
37801
37802     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37803     /** @private */
37804     this.el = Roo.get(els.el);
37805     this.inner = Roo.get(els.inner, true);
37806     this.textEl = Roo.get(this.el.dom.firstChild, true);
37807     this.pnode = Roo.get(els.el.parentNode, true);
37808 //    this.el.on("mousedown", this.onTabMouseDown, this);
37809     this.el.on("click", this.onTabClick, this);
37810     /** @private */
37811     if(config.closable){
37812         var c = Roo.get(els.close, true);
37813         c.dom.title = this.closeText;
37814         c.addClassOnOver("close-over");
37815         c.on("click", this.closeClick, this);
37816      }
37817
37818     this.addEvents({
37819          /**
37820          * @event activate
37821          * Fires when this tab becomes the active tab.
37822          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37823          * @param {Roo.TabPanelItem} this
37824          */
37825         "activate": true,
37826         /**
37827          * @event beforeclose
37828          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37829          * @param {Roo.TabPanelItem} this
37830          * @param {Object} e Set cancel to true on this object to cancel the close.
37831          */
37832         "beforeclose": true,
37833         /**
37834          * @event close
37835          * Fires when this tab is closed.
37836          * @param {Roo.TabPanelItem} this
37837          */
37838          "close": true,
37839         /**
37840          * @event deactivate
37841          * Fires when this tab is no longer the active tab.
37842          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37843          * @param {Roo.TabPanelItem} this
37844          */
37845          "deactivate" : true
37846     });
37847     this.hidden = false;
37848
37849     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37850 };
37851
37852 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37853            {
37854     purgeListeners : function(){
37855        Roo.util.Observable.prototype.purgeListeners.call(this);
37856        this.el.removeAllListeners();
37857     },
37858     /**
37859      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37860      */
37861     show : function(){
37862         this.pnode.addClass("active");
37863         this.showAction();
37864         if(Roo.isOpera){
37865             this.tabPanel.stripWrap.repaint();
37866         }
37867         this.fireEvent("activate", this.tabPanel, this);
37868     },
37869
37870     /**
37871      * Returns true if this tab is the active tab.
37872      * @return {Boolean}
37873      */
37874     isActive : function(){
37875         return this.tabPanel.getActiveTab() == this;
37876     },
37877
37878     /**
37879      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37880      */
37881     hide : function(){
37882         this.pnode.removeClass("active");
37883         this.hideAction();
37884         this.fireEvent("deactivate", this.tabPanel, this);
37885     },
37886
37887     hideAction : function(){
37888         this.bodyEl.hide();
37889         this.bodyEl.setStyle("position", "absolute");
37890         this.bodyEl.setLeft("-20000px");
37891         this.bodyEl.setTop("-20000px");
37892     },
37893
37894     showAction : function(){
37895         this.bodyEl.setStyle("position", "relative");
37896         this.bodyEl.setTop("");
37897         this.bodyEl.setLeft("");
37898         this.bodyEl.show();
37899     },
37900
37901     /**
37902      * Set the tooltip for the tab.
37903      * @param {String} tooltip The tab's tooltip
37904      */
37905     setTooltip : function(text){
37906         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37907             this.textEl.dom.qtip = text;
37908             this.textEl.dom.removeAttribute('title');
37909         }else{
37910             this.textEl.dom.title = text;
37911         }
37912     },
37913
37914     onTabClick : function(e){
37915         e.preventDefault();
37916         this.tabPanel.activate(this.id);
37917     },
37918
37919     onTabMouseDown : function(e){
37920         e.preventDefault();
37921         this.tabPanel.activate(this.id);
37922     },
37923 /*
37924     getWidth : function(){
37925         return this.inner.getWidth();
37926     },
37927
37928     setWidth : function(width){
37929         var iwidth = width - this.pnode.getPadding("lr");
37930         this.inner.setWidth(iwidth);
37931         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37932         this.pnode.setWidth(width);
37933     },
37934 */
37935     /**
37936      * Show or hide the tab
37937      * @param {Boolean} hidden True to hide or false to show.
37938      */
37939     setHidden : function(hidden){
37940         this.hidden = hidden;
37941         this.pnode.setStyle("display", hidden ? "none" : "");
37942     },
37943
37944     /**
37945      * Returns true if this tab is "hidden"
37946      * @return {Boolean}
37947      */
37948     isHidden : function(){
37949         return this.hidden;
37950     },
37951
37952     /**
37953      * Returns the text for this tab
37954      * @return {String}
37955      */
37956     getText : function(){
37957         return this.text;
37958     },
37959     /*
37960     autoSize : function(){
37961         //this.el.beginMeasure();
37962         this.textEl.setWidth(1);
37963         /*
37964          *  #2804 [new] Tabs in Roojs
37965          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37966          */
37967         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37968         //this.el.endMeasure();
37969     //},
37970
37971     /**
37972      * Sets the text for the tab (Note: this also sets the tooltip text)
37973      * @param {String} text The tab's text and tooltip
37974      */
37975     setText : function(text){
37976         this.text = text;
37977         this.textEl.update(text);
37978         this.setTooltip(text);
37979         //if(!this.tabPanel.resizeTabs){
37980         //    this.autoSize();
37981         //}
37982     },
37983     /**
37984      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37985      */
37986     activate : function(){
37987         this.tabPanel.activate(this.id);
37988     },
37989
37990     /**
37991      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37992      */
37993     disable : function(){
37994         if(this.tabPanel.active != this){
37995             this.disabled = true;
37996             this.pnode.addClass("disabled");
37997         }
37998     },
37999
38000     /**
38001      * Enables this TabPanelItem if it was previously disabled.
38002      */
38003     enable : function(){
38004         this.disabled = false;
38005         this.pnode.removeClass("disabled");
38006     },
38007
38008     /**
38009      * Sets the content for this TabPanelItem.
38010      * @param {String} content The content
38011      * @param {Boolean} loadScripts true to look for and load scripts
38012      */
38013     setContent : function(content, loadScripts){
38014         this.bodyEl.update(content, loadScripts);
38015     },
38016
38017     /**
38018      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38019      * @return {Roo.UpdateManager} The UpdateManager
38020      */
38021     getUpdateManager : function(){
38022         return this.bodyEl.getUpdateManager();
38023     },
38024
38025     /**
38026      * Set a URL to be used to load the content for this TabPanelItem.
38027      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38028      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
38029      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
38030      * @return {Roo.UpdateManager} The UpdateManager
38031      */
38032     setUrl : function(url, params, loadOnce){
38033         if(this.refreshDelegate){
38034             this.un('activate', this.refreshDelegate);
38035         }
38036         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38037         this.on("activate", this.refreshDelegate);
38038         return this.bodyEl.getUpdateManager();
38039     },
38040
38041     /** @private */
38042     _handleRefresh : function(url, params, loadOnce){
38043         if(!loadOnce || !this.loaded){
38044             var updater = this.bodyEl.getUpdateManager();
38045             updater.update(url, params, this._setLoaded.createDelegate(this));
38046         }
38047     },
38048
38049     /**
38050      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38051      *   Will fail silently if the setUrl method has not been called.
38052      *   This does not activate the panel, just updates its content.
38053      */
38054     refresh : function(){
38055         if(this.refreshDelegate){
38056            this.loaded = false;
38057            this.refreshDelegate();
38058         }
38059     },
38060
38061     /** @private */
38062     _setLoaded : function(){
38063         this.loaded = true;
38064     },
38065
38066     /** @private */
38067     closeClick : function(e){
38068         var o = {};
38069         e.stopEvent();
38070         this.fireEvent("beforeclose", this, o);
38071         if(o.cancel !== true){
38072             this.tabPanel.removeTab(this.id);
38073         }
38074     },
38075     /**
38076      * The text displayed in the tooltip for the close icon.
38077      * @type String
38078      */
38079     closeText : "Close this tab"
38080 });
38081 /**
38082 *    This script refer to:
38083 *    Title: International Telephone Input
38084 *    Author: Jack O'Connor
38085 *    Code version:  v12.1.12
38086 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38087 **/
38088
38089 Roo.bootstrap.PhoneInputData = function() {
38090     var d = [
38091       [
38092         "Afghanistan (‫افغانستان‬‎)",
38093         "af",
38094         "93"
38095       ],
38096       [
38097         "Albania (Shqipëri)",
38098         "al",
38099         "355"
38100       ],
38101       [
38102         "Algeria (‫الجزائر‬‎)",
38103         "dz",
38104         "213"
38105       ],
38106       [
38107         "American Samoa",
38108         "as",
38109         "1684"
38110       ],
38111       [
38112         "Andorra",
38113         "ad",
38114         "376"
38115       ],
38116       [
38117         "Angola",
38118         "ao",
38119         "244"
38120       ],
38121       [
38122         "Anguilla",
38123         "ai",
38124         "1264"
38125       ],
38126       [
38127         "Antigua and Barbuda",
38128         "ag",
38129         "1268"
38130       ],
38131       [
38132         "Argentina",
38133         "ar",
38134         "54"
38135       ],
38136       [
38137         "Armenia (Հայաստան)",
38138         "am",
38139         "374"
38140       ],
38141       [
38142         "Aruba",
38143         "aw",
38144         "297"
38145       ],
38146       [
38147         "Australia",
38148         "au",
38149         "61",
38150         0
38151       ],
38152       [
38153         "Austria (Österreich)",
38154         "at",
38155         "43"
38156       ],
38157       [
38158         "Azerbaijan (Azərbaycan)",
38159         "az",
38160         "994"
38161       ],
38162       [
38163         "Bahamas",
38164         "bs",
38165         "1242"
38166       ],
38167       [
38168         "Bahrain (‫البحرين‬‎)",
38169         "bh",
38170         "973"
38171       ],
38172       [
38173         "Bangladesh (বাংলাদেশ)",
38174         "bd",
38175         "880"
38176       ],
38177       [
38178         "Barbados",
38179         "bb",
38180         "1246"
38181       ],
38182       [
38183         "Belarus (Беларусь)",
38184         "by",
38185         "375"
38186       ],
38187       [
38188         "Belgium (België)",
38189         "be",
38190         "32"
38191       ],
38192       [
38193         "Belize",
38194         "bz",
38195         "501"
38196       ],
38197       [
38198         "Benin (Bénin)",
38199         "bj",
38200         "229"
38201       ],
38202       [
38203         "Bermuda",
38204         "bm",
38205         "1441"
38206       ],
38207       [
38208         "Bhutan (འབྲུག)",
38209         "bt",
38210         "975"
38211       ],
38212       [
38213         "Bolivia",
38214         "bo",
38215         "591"
38216       ],
38217       [
38218         "Bosnia and Herzegovina (Босна и Херцеговина)",
38219         "ba",
38220         "387"
38221       ],
38222       [
38223         "Botswana",
38224         "bw",
38225         "267"
38226       ],
38227       [
38228         "Brazil (Brasil)",
38229         "br",
38230         "55"
38231       ],
38232       [
38233         "British Indian Ocean Territory",
38234         "io",
38235         "246"
38236       ],
38237       [
38238         "British Virgin Islands",
38239         "vg",
38240         "1284"
38241       ],
38242       [
38243         "Brunei",
38244         "bn",
38245         "673"
38246       ],
38247       [
38248         "Bulgaria (България)",
38249         "bg",
38250         "359"
38251       ],
38252       [
38253         "Burkina Faso",
38254         "bf",
38255         "226"
38256       ],
38257       [
38258         "Burundi (Uburundi)",
38259         "bi",
38260         "257"
38261       ],
38262       [
38263         "Cambodia (កម្ពុជា)",
38264         "kh",
38265         "855"
38266       ],
38267       [
38268         "Cameroon (Cameroun)",
38269         "cm",
38270         "237"
38271       ],
38272       [
38273         "Canada",
38274         "ca",
38275         "1",
38276         1,
38277         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38278       ],
38279       [
38280         "Cape Verde (Kabu Verdi)",
38281         "cv",
38282         "238"
38283       ],
38284       [
38285         "Caribbean Netherlands",
38286         "bq",
38287         "599",
38288         1
38289       ],
38290       [
38291         "Cayman Islands",
38292         "ky",
38293         "1345"
38294       ],
38295       [
38296         "Central African Republic (République centrafricaine)",
38297         "cf",
38298         "236"
38299       ],
38300       [
38301         "Chad (Tchad)",
38302         "td",
38303         "235"
38304       ],
38305       [
38306         "Chile",
38307         "cl",
38308         "56"
38309       ],
38310       [
38311         "China (中国)",
38312         "cn",
38313         "86"
38314       ],
38315       [
38316         "Christmas Island",
38317         "cx",
38318         "61",
38319         2
38320       ],
38321       [
38322         "Cocos (Keeling) Islands",
38323         "cc",
38324         "61",
38325         1
38326       ],
38327       [
38328         "Colombia",
38329         "co",
38330         "57"
38331       ],
38332       [
38333         "Comoros (‫جزر القمر‬‎)",
38334         "km",
38335         "269"
38336       ],
38337       [
38338         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38339         "cd",
38340         "243"
38341       ],
38342       [
38343         "Congo (Republic) (Congo-Brazzaville)",
38344         "cg",
38345         "242"
38346       ],
38347       [
38348         "Cook Islands",
38349         "ck",
38350         "682"
38351       ],
38352       [
38353         "Costa Rica",
38354         "cr",
38355         "506"
38356       ],
38357       [
38358         "Côte d’Ivoire",
38359         "ci",
38360         "225"
38361       ],
38362       [
38363         "Croatia (Hrvatska)",
38364         "hr",
38365         "385"
38366       ],
38367       [
38368         "Cuba",
38369         "cu",
38370         "53"
38371       ],
38372       [
38373         "Curaçao",
38374         "cw",
38375         "599",
38376         0
38377       ],
38378       [
38379         "Cyprus (Κύπρος)",
38380         "cy",
38381         "357"
38382       ],
38383       [
38384         "Czech Republic (Česká republika)",
38385         "cz",
38386         "420"
38387       ],
38388       [
38389         "Denmark (Danmark)",
38390         "dk",
38391         "45"
38392       ],
38393       [
38394         "Djibouti",
38395         "dj",
38396         "253"
38397       ],
38398       [
38399         "Dominica",
38400         "dm",
38401         "1767"
38402       ],
38403       [
38404         "Dominican Republic (República Dominicana)",
38405         "do",
38406         "1",
38407         2,
38408         ["809", "829", "849"]
38409       ],
38410       [
38411         "Ecuador",
38412         "ec",
38413         "593"
38414       ],
38415       [
38416         "Egypt (‫مصر‬‎)",
38417         "eg",
38418         "20"
38419       ],
38420       [
38421         "El Salvador",
38422         "sv",
38423         "503"
38424       ],
38425       [
38426         "Equatorial Guinea (Guinea Ecuatorial)",
38427         "gq",
38428         "240"
38429       ],
38430       [
38431         "Eritrea",
38432         "er",
38433         "291"
38434       ],
38435       [
38436         "Estonia (Eesti)",
38437         "ee",
38438         "372"
38439       ],
38440       [
38441         "Ethiopia",
38442         "et",
38443         "251"
38444       ],
38445       [
38446         "Falkland Islands (Islas Malvinas)",
38447         "fk",
38448         "500"
38449       ],
38450       [
38451         "Faroe Islands (Føroyar)",
38452         "fo",
38453         "298"
38454       ],
38455       [
38456         "Fiji",
38457         "fj",
38458         "679"
38459       ],
38460       [
38461         "Finland (Suomi)",
38462         "fi",
38463         "358",
38464         0
38465       ],
38466       [
38467         "France",
38468         "fr",
38469         "33"
38470       ],
38471       [
38472         "French Guiana (Guyane française)",
38473         "gf",
38474         "594"
38475       ],
38476       [
38477         "French Polynesia (Polynésie française)",
38478         "pf",
38479         "689"
38480       ],
38481       [
38482         "Gabon",
38483         "ga",
38484         "241"
38485       ],
38486       [
38487         "Gambia",
38488         "gm",
38489         "220"
38490       ],
38491       [
38492         "Georgia (საქართველო)",
38493         "ge",
38494         "995"
38495       ],
38496       [
38497         "Germany (Deutschland)",
38498         "de",
38499         "49"
38500       ],
38501       [
38502         "Ghana (Gaana)",
38503         "gh",
38504         "233"
38505       ],
38506       [
38507         "Gibraltar",
38508         "gi",
38509         "350"
38510       ],
38511       [
38512         "Greece (Ελλάδα)",
38513         "gr",
38514         "30"
38515       ],
38516       [
38517         "Greenland (Kalaallit Nunaat)",
38518         "gl",
38519         "299"
38520       ],
38521       [
38522         "Grenada",
38523         "gd",
38524         "1473"
38525       ],
38526       [
38527         "Guadeloupe",
38528         "gp",
38529         "590",
38530         0
38531       ],
38532       [
38533         "Guam",
38534         "gu",
38535         "1671"
38536       ],
38537       [
38538         "Guatemala",
38539         "gt",
38540         "502"
38541       ],
38542       [
38543         "Guernsey",
38544         "gg",
38545         "44",
38546         1
38547       ],
38548       [
38549         "Guinea (Guinée)",
38550         "gn",
38551         "224"
38552       ],
38553       [
38554         "Guinea-Bissau (Guiné Bissau)",
38555         "gw",
38556         "245"
38557       ],
38558       [
38559         "Guyana",
38560         "gy",
38561         "592"
38562       ],
38563       [
38564         "Haiti",
38565         "ht",
38566         "509"
38567       ],
38568       [
38569         "Honduras",
38570         "hn",
38571         "504"
38572       ],
38573       [
38574         "Hong Kong (香港)",
38575         "hk",
38576         "852"
38577       ],
38578       [
38579         "Hungary (Magyarország)",
38580         "hu",
38581         "36"
38582       ],
38583       [
38584         "Iceland (Ísland)",
38585         "is",
38586         "354"
38587       ],
38588       [
38589         "India (भारत)",
38590         "in",
38591         "91"
38592       ],
38593       [
38594         "Indonesia",
38595         "id",
38596         "62"
38597       ],
38598       [
38599         "Iran (‫ایران‬‎)",
38600         "ir",
38601         "98"
38602       ],
38603       [
38604         "Iraq (‫العراق‬‎)",
38605         "iq",
38606         "964"
38607       ],
38608       [
38609         "Ireland",
38610         "ie",
38611         "353"
38612       ],
38613       [
38614         "Isle of Man",
38615         "im",
38616         "44",
38617         2
38618       ],
38619       [
38620         "Israel (‫ישראל‬‎)",
38621         "il",
38622         "972"
38623       ],
38624       [
38625         "Italy (Italia)",
38626         "it",
38627         "39",
38628         0
38629       ],
38630       [
38631         "Jamaica",
38632         "jm",
38633         "1876"
38634       ],
38635       [
38636         "Japan (日本)",
38637         "jp",
38638         "81"
38639       ],
38640       [
38641         "Jersey",
38642         "je",
38643         "44",
38644         3
38645       ],
38646       [
38647         "Jordan (‫الأردن‬‎)",
38648         "jo",
38649         "962"
38650       ],
38651       [
38652         "Kazakhstan (Казахстан)",
38653         "kz",
38654         "7",
38655         1
38656       ],
38657       [
38658         "Kenya",
38659         "ke",
38660         "254"
38661       ],
38662       [
38663         "Kiribati",
38664         "ki",
38665         "686"
38666       ],
38667       [
38668         "Kosovo",
38669         "xk",
38670         "383"
38671       ],
38672       [
38673         "Kuwait (‫الكويت‬‎)",
38674         "kw",
38675         "965"
38676       ],
38677       [
38678         "Kyrgyzstan (Кыргызстан)",
38679         "kg",
38680         "996"
38681       ],
38682       [
38683         "Laos (ລາວ)",
38684         "la",
38685         "856"
38686       ],
38687       [
38688         "Latvia (Latvija)",
38689         "lv",
38690         "371"
38691       ],
38692       [
38693         "Lebanon (‫لبنان‬‎)",
38694         "lb",
38695         "961"
38696       ],
38697       [
38698         "Lesotho",
38699         "ls",
38700         "266"
38701       ],
38702       [
38703         "Liberia",
38704         "lr",
38705         "231"
38706       ],
38707       [
38708         "Libya (‫ليبيا‬‎)",
38709         "ly",
38710         "218"
38711       ],
38712       [
38713         "Liechtenstein",
38714         "li",
38715         "423"
38716       ],
38717       [
38718         "Lithuania (Lietuva)",
38719         "lt",
38720         "370"
38721       ],
38722       [
38723         "Luxembourg",
38724         "lu",
38725         "352"
38726       ],
38727       [
38728         "Macau (澳門)",
38729         "mo",
38730         "853"
38731       ],
38732       [
38733         "Macedonia (FYROM) (Македонија)",
38734         "mk",
38735         "389"
38736       ],
38737       [
38738         "Madagascar (Madagasikara)",
38739         "mg",
38740         "261"
38741       ],
38742       [
38743         "Malawi",
38744         "mw",
38745         "265"
38746       ],
38747       [
38748         "Malaysia",
38749         "my",
38750         "60"
38751       ],
38752       [
38753         "Maldives",
38754         "mv",
38755         "960"
38756       ],
38757       [
38758         "Mali",
38759         "ml",
38760         "223"
38761       ],
38762       [
38763         "Malta",
38764         "mt",
38765         "356"
38766       ],
38767       [
38768         "Marshall Islands",
38769         "mh",
38770         "692"
38771       ],
38772       [
38773         "Martinique",
38774         "mq",
38775         "596"
38776       ],
38777       [
38778         "Mauritania (‫موريتانيا‬‎)",
38779         "mr",
38780         "222"
38781       ],
38782       [
38783         "Mauritius (Moris)",
38784         "mu",
38785         "230"
38786       ],
38787       [
38788         "Mayotte",
38789         "yt",
38790         "262",
38791         1
38792       ],
38793       [
38794         "Mexico (México)",
38795         "mx",
38796         "52"
38797       ],
38798       [
38799         "Micronesia",
38800         "fm",
38801         "691"
38802       ],
38803       [
38804         "Moldova (Republica Moldova)",
38805         "md",
38806         "373"
38807       ],
38808       [
38809         "Monaco",
38810         "mc",
38811         "377"
38812       ],
38813       [
38814         "Mongolia (Монгол)",
38815         "mn",
38816         "976"
38817       ],
38818       [
38819         "Montenegro (Crna Gora)",
38820         "me",
38821         "382"
38822       ],
38823       [
38824         "Montserrat",
38825         "ms",
38826         "1664"
38827       ],
38828       [
38829         "Morocco (‫المغرب‬‎)",
38830         "ma",
38831         "212",
38832         0
38833       ],
38834       [
38835         "Mozambique (Moçambique)",
38836         "mz",
38837         "258"
38838       ],
38839       [
38840         "Myanmar (Burma) (မြန်မာ)",
38841         "mm",
38842         "95"
38843       ],
38844       [
38845         "Namibia (Namibië)",
38846         "na",
38847         "264"
38848       ],
38849       [
38850         "Nauru",
38851         "nr",
38852         "674"
38853       ],
38854       [
38855         "Nepal (नेपाल)",
38856         "np",
38857         "977"
38858       ],
38859       [
38860         "Netherlands (Nederland)",
38861         "nl",
38862         "31"
38863       ],
38864       [
38865         "New Caledonia (Nouvelle-Calédonie)",
38866         "nc",
38867         "687"
38868       ],
38869       [
38870         "New Zealand",
38871         "nz",
38872         "64"
38873       ],
38874       [
38875         "Nicaragua",
38876         "ni",
38877         "505"
38878       ],
38879       [
38880         "Niger (Nijar)",
38881         "ne",
38882         "227"
38883       ],
38884       [
38885         "Nigeria",
38886         "ng",
38887         "234"
38888       ],
38889       [
38890         "Niue",
38891         "nu",
38892         "683"
38893       ],
38894       [
38895         "Norfolk Island",
38896         "nf",
38897         "672"
38898       ],
38899       [
38900         "North Korea (조선 민주주의 인민 공화국)",
38901         "kp",
38902         "850"
38903       ],
38904       [
38905         "Northern Mariana Islands",
38906         "mp",
38907         "1670"
38908       ],
38909       [
38910         "Norway (Norge)",
38911         "no",
38912         "47",
38913         0
38914       ],
38915       [
38916         "Oman (‫عُمان‬‎)",
38917         "om",
38918         "968"
38919       ],
38920       [
38921         "Pakistan (‫پاکستان‬‎)",
38922         "pk",
38923         "92"
38924       ],
38925       [
38926         "Palau",
38927         "pw",
38928         "680"
38929       ],
38930       [
38931         "Palestine (‫فلسطين‬‎)",
38932         "ps",
38933         "970"
38934       ],
38935       [
38936         "Panama (Panamá)",
38937         "pa",
38938         "507"
38939       ],
38940       [
38941         "Papua New Guinea",
38942         "pg",
38943         "675"
38944       ],
38945       [
38946         "Paraguay",
38947         "py",
38948         "595"
38949       ],
38950       [
38951         "Peru (Perú)",
38952         "pe",
38953         "51"
38954       ],
38955       [
38956         "Philippines",
38957         "ph",
38958         "63"
38959       ],
38960       [
38961         "Poland (Polska)",
38962         "pl",
38963         "48"
38964       ],
38965       [
38966         "Portugal",
38967         "pt",
38968         "351"
38969       ],
38970       [
38971         "Puerto Rico",
38972         "pr",
38973         "1",
38974         3,
38975         ["787", "939"]
38976       ],
38977       [
38978         "Qatar (‫قطر‬‎)",
38979         "qa",
38980         "974"
38981       ],
38982       [
38983         "Réunion (La Réunion)",
38984         "re",
38985         "262",
38986         0
38987       ],
38988       [
38989         "Romania (România)",
38990         "ro",
38991         "40"
38992       ],
38993       [
38994         "Russia (Россия)",
38995         "ru",
38996         "7",
38997         0
38998       ],
38999       [
39000         "Rwanda",
39001         "rw",
39002         "250"
39003       ],
39004       [
39005         "Saint Barthélemy",
39006         "bl",
39007         "590",
39008         1
39009       ],
39010       [
39011         "Saint Helena",
39012         "sh",
39013         "290"
39014       ],
39015       [
39016         "Saint Kitts and Nevis",
39017         "kn",
39018         "1869"
39019       ],
39020       [
39021         "Saint Lucia",
39022         "lc",
39023         "1758"
39024       ],
39025       [
39026         "Saint Martin (Saint-Martin (partie française))",
39027         "mf",
39028         "590",
39029         2
39030       ],
39031       [
39032         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39033         "pm",
39034         "508"
39035       ],
39036       [
39037         "Saint Vincent and the Grenadines",
39038         "vc",
39039         "1784"
39040       ],
39041       [
39042         "Samoa",
39043         "ws",
39044         "685"
39045       ],
39046       [
39047         "San Marino",
39048         "sm",
39049         "378"
39050       ],
39051       [
39052         "São Tomé and Príncipe (São Tomé e Príncipe)",
39053         "st",
39054         "239"
39055       ],
39056       [
39057         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39058         "sa",
39059         "966"
39060       ],
39061       [
39062         "Senegal (Sénégal)",
39063         "sn",
39064         "221"
39065       ],
39066       [
39067         "Serbia (Србија)",
39068         "rs",
39069         "381"
39070       ],
39071       [
39072         "Seychelles",
39073         "sc",
39074         "248"
39075       ],
39076       [
39077         "Sierra Leone",
39078         "sl",
39079         "232"
39080       ],
39081       [
39082         "Singapore",
39083         "sg",
39084         "65"
39085       ],
39086       [
39087         "Sint Maarten",
39088         "sx",
39089         "1721"
39090       ],
39091       [
39092         "Slovakia (Slovensko)",
39093         "sk",
39094         "421"
39095       ],
39096       [
39097         "Slovenia (Slovenija)",
39098         "si",
39099         "386"
39100       ],
39101       [
39102         "Solomon Islands",
39103         "sb",
39104         "677"
39105       ],
39106       [
39107         "Somalia (Soomaaliya)",
39108         "so",
39109         "252"
39110       ],
39111       [
39112         "South Africa",
39113         "za",
39114         "27"
39115       ],
39116       [
39117         "South Korea (대한민국)",
39118         "kr",
39119         "82"
39120       ],
39121       [
39122         "South Sudan (‫جنوب السودان‬‎)",
39123         "ss",
39124         "211"
39125       ],
39126       [
39127         "Spain (España)",
39128         "es",
39129         "34"
39130       ],
39131       [
39132         "Sri Lanka (ශ්‍රී ලංකාව)",
39133         "lk",
39134         "94"
39135       ],
39136       [
39137         "Sudan (‫السودان‬‎)",
39138         "sd",
39139         "249"
39140       ],
39141       [
39142         "Suriname",
39143         "sr",
39144         "597"
39145       ],
39146       [
39147         "Svalbard and Jan Mayen",
39148         "sj",
39149         "47",
39150         1
39151       ],
39152       [
39153         "Swaziland",
39154         "sz",
39155         "268"
39156       ],
39157       [
39158         "Sweden (Sverige)",
39159         "se",
39160         "46"
39161       ],
39162       [
39163         "Switzerland (Schweiz)",
39164         "ch",
39165         "41"
39166       ],
39167       [
39168         "Syria (‫سوريا‬‎)",
39169         "sy",
39170         "963"
39171       ],
39172       [
39173         "Taiwan (台灣)",
39174         "tw",
39175         "886"
39176       ],
39177       [
39178         "Tajikistan",
39179         "tj",
39180         "992"
39181       ],
39182       [
39183         "Tanzania",
39184         "tz",
39185         "255"
39186       ],
39187       [
39188         "Thailand (ไทย)",
39189         "th",
39190         "66"
39191       ],
39192       [
39193         "Timor-Leste",
39194         "tl",
39195         "670"
39196       ],
39197       [
39198         "Togo",
39199         "tg",
39200         "228"
39201       ],
39202       [
39203         "Tokelau",
39204         "tk",
39205         "690"
39206       ],
39207       [
39208         "Tonga",
39209         "to",
39210         "676"
39211       ],
39212       [
39213         "Trinidad and Tobago",
39214         "tt",
39215         "1868"
39216       ],
39217       [
39218         "Tunisia (‫تونس‬‎)",
39219         "tn",
39220         "216"
39221       ],
39222       [
39223         "Turkey (Türkiye)",
39224         "tr",
39225         "90"
39226       ],
39227       [
39228         "Turkmenistan",
39229         "tm",
39230         "993"
39231       ],
39232       [
39233         "Turks and Caicos Islands",
39234         "tc",
39235         "1649"
39236       ],
39237       [
39238         "Tuvalu",
39239         "tv",
39240         "688"
39241       ],
39242       [
39243         "U.S. Virgin Islands",
39244         "vi",
39245         "1340"
39246       ],
39247       [
39248         "Uganda",
39249         "ug",
39250         "256"
39251       ],
39252       [
39253         "Ukraine (Україна)",
39254         "ua",
39255         "380"
39256       ],
39257       [
39258         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39259         "ae",
39260         "971"
39261       ],
39262       [
39263         "United Kingdom",
39264         "gb",
39265         "44",
39266         0
39267       ],
39268       [
39269         "United States",
39270         "us",
39271         "1",
39272         0
39273       ],
39274       [
39275         "Uruguay",
39276         "uy",
39277         "598"
39278       ],
39279       [
39280         "Uzbekistan (Oʻzbekiston)",
39281         "uz",
39282         "998"
39283       ],
39284       [
39285         "Vanuatu",
39286         "vu",
39287         "678"
39288       ],
39289       [
39290         "Vatican City (Città del Vaticano)",
39291         "va",
39292         "39",
39293         1
39294       ],
39295       [
39296         "Venezuela",
39297         "ve",
39298         "58"
39299       ],
39300       [
39301         "Vietnam (Việt Nam)",
39302         "vn",
39303         "84"
39304       ],
39305       [
39306         "Wallis and Futuna (Wallis-et-Futuna)",
39307         "wf",
39308         "681"
39309       ],
39310       [
39311         "Western Sahara (‫الصحراء الغربية‬‎)",
39312         "eh",
39313         "212",
39314         1
39315       ],
39316       [
39317         "Yemen (‫اليمن‬‎)",
39318         "ye",
39319         "967"
39320       ],
39321       [
39322         "Zambia",
39323         "zm",
39324         "260"
39325       ],
39326       [
39327         "Zimbabwe",
39328         "zw",
39329         "263"
39330       ],
39331       [
39332         "Åland Islands",
39333         "ax",
39334         "358",
39335         1
39336       ]
39337   ];
39338   
39339   return d;
39340 }/**
39341 *    This script refer to:
39342 *    Title: International Telephone Input
39343 *    Author: Jack O'Connor
39344 *    Code version:  v12.1.12
39345 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39346 **/
39347
39348 /**
39349  * @class Roo.bootstrap.PhoneInput
39350  * @extends Roo.bootstrap.TriggerField
39351  * An input with International dial-code selection
39352  
39353  * @cfg {String} defaultDialCode default '+852'
39354  * @cfg {Array} preferedCountries default []
39355   
39356  * @constructor
39357  * Create a new PhoneInput.
39358  * @param {Object} config Configuration options
39359  */
39360
39361 Roo.bootstrap.PhoneInput = function(config) {
39362     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39363 };
39364
39365 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39366         
39367         listWidth: undefined,
39368         
39369         selectedClass: 'active',
39370         
39371         invalidClass : "has-warning",
39372         
39373         validClass: 'has-success',
39374         
39375         allowed: '0123456789',
39376         
39377         /**
39378          * @cfg {String} defaultDialCode The default dial code when initializing the input
39379          */
39380         defaultDialCode: '+852',
39381         
39382         /**
39383          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39384          */
39385         preferedCountries: false,
39386         
39387         getAutoCreate : function()
39388         {
39389             var data = Roo.bootstrap.PhoneInputData();
39390             var align = this.labelAlign || this.parentLabelAlign();
39391             var id = Roo.id();
39392             
39393             this.allCountries = [];
39394             this.dialCodeMapping = [];
39395             
39396             for (var i = 0; i < data.length; i++) {
39397               var c = data[i];
39398               this.allCountries[i] = {
39399                 name: c[0],
39400                 iso2: c[1],
39401                 dialCode: c[2],
39402                 priority: c[3] || 0,
39403                 areaCodes: c[4] || null
39404               };
39405               this.dialCodeMapping[c[2]] = {
39406                   name: c[0],
39407                   iso2: c[1],
39408                   priority: c[3] || 0,
39409                   areaCodes: c[4] || null
39410               };
39411             }
39412             
39413             var cfg = {
39414                 cls: 'form-group',
39415                 cn: []
39416             };
39417             
39418             var input =  {
39419                 tag: 'input',
39420                 id : id,
39421                 cls : 'form-control tel-input',
39422                 autocomplete: 'new-password'
39423             };
39424             
39425             var hiddenInput = {
39426                 tag: 'input',
39427                 type: 'hidden',
39428                 cls: 'hidden-tel-input'
39429             };
39430             
39431             if (this.name) {
39432                 hiddenInput.name = this.name;
39433             }
39434             
39435             if (this.disabled) {
39436                 input.disabled = true;
39437             }
39438             
39439             var flag_container = {
39440                 tag: 'div',
39441                 cls: 'flag-box',
39442                 cn: [
39443                     {
39444                         tag: 'div',
39445                         cls: 'flag'
39446                     },
39447                     {
39448                         tag: 'div',
39449                         cls: 'caret'
39450                     }
39451                 ]
39452             };
39453             
39454             var box = {
39455                 tag: 'div',
39456                 cls: this.hasFeedback ? 'has-feedback' : '',
39457                 cn: [
39458                     hiddenInput,
39459                     input,
39460                     {
39461                         tag: 'input',
39462                         cls: 'dial-code-holder',
39463                         disabled: true
39464                     }
39465                 ]
39466             };
39467             
39468             var container = {
39469                 cls: 'roo-select2-container input-group',
39470                 cn: [
39471                     flag_container,
39472                     box
39473                 ]
39474             };
39475             
39476             if (this.fieldLabel.length) {
39477                 var indicator = {
39478                     tag: 'i',
39479                     tooltip: 'This field is required'
39480                 };
39481                 
39482                 var label = {
39483                     tag: 'label',
39484                     'for':  id,
39485                     cls: 'control-label',
39486                     cn: []
39487                 };
39488                 
39489                 var label_text = {
39490                     tag: 'span',
39491                     html: this.fieldLabel
39492                 };
39493                 
39494                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39495                 label.cn = [
39496                     indicator,
39497                     label_text
39498                 ];
39499                 
39500                 if(this.indicatorpos == 'right') {
39501                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39502                     label.cn = [
39503                         label_text,
39504                         indicator
39505                     ];
39506                 }
39507                 
39508                 if(align == 'left') {
39509                     container = {
39510                         tag: 'div',
39511                         cn: [
39512                             container
39513                         ]
39514                     };
39515                     
39516                     if(this.labelWidth > 12){
39517                         label.style = "width: " + this.labelWidth + 'px';
39518                     }
39519                     if(this.labelWidth < 13 && this.labelmd == 0){
39520                         this.labelmd = this.labelWidth;
39521                     }
39522                     if(this.labellg > 0){
39523                         label.cls += ' col-lg-' + this.labellg;
39524                         input.cls += ' col-lg-' + (12 - this.labellg);
39525                     }
39526                     if(this.labelmd > 0){
39527                         label.cls += ' col-md-' + this.labelmd;
39528                         container.cls += ' col-md-' + (12 - this.labelmd);
39529                     }
39530                     if(this.labelsm > 0){
39531                         label.cls += ' col-sm-' + this.labelsm;
39532                         container.cls += ' col-sm-' + (12 - this.labelsm);
39533                     }
39534                     if(this.labelxs > 0){
39535                         label.cls += ' col-xs-' + this.labelxs;
39536                         container.cls += ' col-xs-' + (12 - this.labelxs);
39537                     }
39538                 }
39539             }
39540             
39541             cfg.cn = [
39542                 label,
39543                 container
39544             ];
39545             
39546             var settings = this;
39547             
39548             ['xs','sm','md','lg'].map(function(size){
39549                 if (settings[size]) {
39550                     cfg.cls += ' col-' + size + '-' + settings[size];
39551                 }
39552             });
39553             
39554             this.store = new Roo.data.Store({
39555                 proxy : new Roo.data.MemoryProxy({}),
39556                 reader : new Roo.data.JsonReader({
39557                     fields : [
39558                         {
39559                             'name' : 'name',
39560                             'type' : 'string'
39561                         },
39562                         {
39563                             'name' : 'iso2',
39564                             'type' : 'string'
39565                         },
39566                         {
39567                             'name' : 'dialCode',
39568                             'type' : 'string'
39569                         },
39570                         {
39571                             'name' : 'priority',
39572                             'type' : 'string'
39573                         },
39574                         {
39575                             'name' : 'areaCodes',
39576                             'type' : 'string'
39577                         }
39578                     ]
39579                 })
39580             });
39581             
39582             if(!this.preferedCountries) {
39583                 this.preferedCountries = [
39584                     'hk',
39585                     'gb',
39586                     'us'
39587                 ];
39588             }
39589             
39590             var p = this.preferedCountries.reverse();
39591             
39592             if(p) {
39593                 for (var i = 0; i < p.length; i++) {
39594                     for (var j = 0; j < this.allCountries.length; j++) {
39595                         if(this.allCountries[j].iso2 == p[i]) {
39596                             var t = this.allCountries[j];
39597                             this.allCountries.splice(j,1);
39598                             this.allCountries.unshift(t);
39599                         }
39600                     } 
39601                 }
39602             }
39603             
39604             this.store.proxy.data = {
39605                 success: true,
39606                 data: this.allCountries
39607             };
39608             
39609             return cfg;
39610         },
39611         
39612         initEvents : function()
39613         {
39614             this.createList();
39615             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39616             
39617             this.indicator = this.indicatorEl();
39618             this.flag = this.flagEl();
39619             this.dialCodeHolder = this.dialCodeHolderEl();
39620             
39621             this.trigger = this.el.select('div.flag-box',true).first();
39622             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39623             
39624             var _this = this;
39625             
39626             (function(){
39627                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39628                 _this.list.setWidth(lw);
39629             }).defer(100);
39630             
39631             this.list.on('mouseover', this.onViewOver, this);
39632             this.list.on('mousemove', this.onViewMove, this);
39633             this.inputEl().on("keyup", this.onKeyUp, this);
39634             
39635             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39636
39637             this.view = new Roo.View(this.list, this.tpl, {
39638                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39639             });
39640             
39641             this.view.on('click', this.onViewClick, this);
39642             this.setValue(this.defaultDialCode);
39643         },
39644         
39645         onTriggerClick : function(e)
39646         {
39647             Roo.log('trigger click');
39648             if(this.disabled){
39649                 return;
39650             }
39651             
39652             if(this.isExpanded()){
39653                 this.collapse();
39654                 this.hasFocus = false;
39655             }else {
39656                 this.store.load({});
39657                 this.hasFocus = true;
39658                 this.expand();
39659             }
39660         },
39661         
39662         isExpanded : function()
39663         {
39664             return this.list.isVisible();
39665         },
39666         
39667         collapse : function()
39668         {
39669             if(!this.isExpanded()){
39670                 return;
39671             }
39672             this.list.hide();
39673             Roo.get(document).un('mousedown', this.collapseIf, this);
39674             Roo.get(document).un('mousewheel', this.collapseIf, this);
39675             this.fireEvent('collapse', this);
39676             this.validate();
39677         },
39678         
39679         expand : function()
39680         {
39681             Roo.log('expand');
39682
39683             if(this.isExpanded() || !this.hasFocus){
39684                 return;
39685             }
39686             
39687             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39688             this.list.setWidth(lw);
39689             
39690             this.list.show();
39691             this.restrictHeight();
39692             
39693             Roo.get(document).on('mousedown', this.collapseIf, this);
39694             Roo.get(document).on('mousewheel', this.collapseIf, this);
39695             
39696             this.fireEvent('expand', this);
39697         },
39698         
39699         restrictHeight : function()
39700         {
39701             this.list.alignTo(this.inputEl(), this.listAlign);
39702             this.list.alignTo(this.inputEl(), this.listAlign);
39703         },
39704         
39705         onViewOver : function(e, t)
39706         {
39707             if(this.inKeyMode){
39708                 return;
39709             }
39710             var item = this.view.findItemFromChild(t);
39711             
39712             if(item){
39713                 var index = this.view.indexOf(item);
39714                 this.select(index, false);
39715             }
39716         },
39717
39718         // private
39719         onViewClick : function(view, doFocus, el, e)
39720         {
39721             var index = this.view.getSelectedIndexes()[0];
39722             
39723             var r = this.store.getAt(index);
39724             
39725             if(r){
39726                 this.onSelect(r, index);
39727             }
39728             if(doFocus !== false && !this.blockFocus){
39729                 this.inputEl().focus();
39730             }
39731         },
39732         
39733         onViewMove : function(e, t)
39734         {
39735             this.inKeyMode = false;
39736         },
39737         
39738         select : function(index, scrollIntoView)
39739         {
39740             this.selectedIndex = index;
39741             this.view.select(index);
39742             if(scrollIntoView !== false){
39743                 var el = this.view.getNode(index);
39744                 if(el){
39745                     this.list.scrollChildIntoView(el, false);
39746                 }
39747             }
39748         },
39749         
39750         createList : function()
39751         {
39752             this.list = Roo.get(document.body).createChild({
39753                 tag: 'ul',
39754                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39755                 style: 'display:none'
39756             });
39757             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39758         },
39759         
39760         collapseIf : function(e)
39761         {
39762             var in_combo  = e.within(this.el);
39763             var in_list =  e.within(this.list);
39764             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39765             
39766             if (in_combo || in_list || is_list) {
39767                 return;
39768             }
39769             this.collapse();
39770         },
39771         
39772         onSelect : function(record, index)
39773         {
39774             if(this.fireEvent('beforeselect', this, record, index) !== false){
39775                 
39776                 this.setFlagClass(record.data.iso2);
39777                 this.setDialCode(record.data.dialCode);
39778                 this.hasFocus = false;
39779                 this.collapse();
39780                 this.fireEvent('select', this, record, index);
39781             }
39782         },
39783         
39784         flagEl : function()
39785         {
39786             var flag = this.el.select('div.flag',true).first();
39787             if(!flag){
39788                 return false;
39789             }
39790             return flag;
39791         },
39792         
39793         dialCodeHolderEl : function()
39794         {
39795             var d = this.el.select('input.dial-code-holder',true).first();
39796             if(!d){
39797                 return false;
39798             }
39799             return d;
39800         },
39801         
39802         setDialCode : function(v)
39803         {
39804             this.dialCodeHolder.dom.value = '+'+v;
39805         },
39806         
39807         setFlagClass : function(n)
39808         {
39809             this.flag.dom.className = 'flag '+n;
39810         },
39811         
39812         getValue : function()
39813         {
39814             var v = this.inputEl().getValue();
39815             if(this.dialCodeHolder) {
39816                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39817             }
39818             return v;
39819         },
39820         
39821         setValue : function(v)
39822         {
39823             var d = this.getDialCode(v);
39824             
39825             //invalid dial code
39826             if(v.length == 0 || !d || d.length == 0) {
39827                 if(this.rendered){
39828                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39829                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39830                 }
39831                 return;
39832             }
39833             
39834             //valid dial code
39835             this.setFlagClass(this.dialCodeMapping[d].iso2);
39836             this.setDialCode(d);
39837             this.inputEl().dom.value = v.replace('+'+d,'');
39838             this.hiddenEl().dom.value = this.getValue();
39839             
39840             this.validate();
39841         },
39842         
39843         getDialCode : function(v = '')
39844         {
39845             if (v.length == 0) {
39846                 return this.dialCodeHolder.dom.value;
39847             }
39848             
39849             var dialCode = "";
39850             if (v.charAt(0) != "+") {
39851                 return false;
39852             }
39853             var numericChars = "";
39854             for (var i = 1; i < v.length; i++) {
39855               var c = v.charAt(i);
39856               if (!isNaN(c)) {
39857                 numericChars += c;
39858                 if (this.dialCodeMapping[numericChars]) {
39859                   dialCode = v.substr(1, i);
39860                 }
39861                 if (numericChars.length == 4) {
39862                   break;
39863                 }
39864               }
39865             }
39866             return dialCode;
39867         },
39868         
39869         reset : function()
39870         {
39871             this.setValue(this.defaultDialCode);
39872             this.validate();
39873         },
39874         
39875         hiddenEl : function()
39876         {
39877             return this.el.select('input.hidden-tel-input',true).first();
39878         },
39879         
39880         onKeyUp : function(e){
39881             
39882             var k = e.getKey();
39883             var c = e.getCharCode();
39884             
39885             if(
39886                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39887                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39888             ){
39889                 e.stopEvent();
39890             }
39891             
39892             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39893             //     return;
39894             // }
39895             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39896                 e.stopEvent();
39897             }
39898             
39899             this.setValue(this.getValue());
39900         }
39901         
39902 });
39903 /**
39904  * @class Roo.bootstrap.MoneyField
39905  * @extends Roo.bootstrap.ComboBox
39906  * Bootstrap MoneyField class
39907  * 
39908  * @constructor
39909  * Create a new MoneyField.
39910  * @param {Object} config Configuration options
39911  */
39912
39913 Roo.bootstrap.MoneyField = function(config) {
39914     
39915     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39916     
39917 };
39918
39919 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39920     
39921     /**
39922      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39923      */
39924     allowDecimals : true,
39925     /**
39926      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39927      */
39928     decimalSeparator : ".",
39929     /**
39930      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39931      */
39932     decimalPrecision : 2,
39933     /**
39934      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39935      */
39936     allowNegative : true,
39937     /**
39938      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39939      */
39940     minValue : Number.NEGATIVE_INFINITY,
39941     /**
39942      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39943      */
39944     maxValue : Number.MAX_VALUE,
39945     /**
39946      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39947      */
39948     minText : "The minimum value for this field is {0}",
39949     /**
39950      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39951      */
39952     maxText : "The maximum value for this field is {0}",
39953     /**
39954      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39955      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39956      */
39957     nanText : "{0} is not a valid number",
39958     /**
39959      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39960      */
39961     castInt : true,
39962     
39963     inputlg : 9,
39964     inputmd : 9,
39965     inputsm : 9,
39966     inputxs : 6,
39967     
39968     store : false,
39969     
39970     getAutoCreate : function()
39971     {
39972         var align = this.labelAlign || this.parentLabelAlign();
39973         
39974         var id = Roo.id();
39975
39976         var cfg = {
39977             cls: 'form-group',
39978             cn: []
39979         };
39980
39981         var input =  {
39982             tag: 'input',
39983             id : id,
39984             cls : 'form-control roo-money-amount-input',
39985             autocomplete: 'new-password'
39986         };
39987         
39988         if (this.name) {
39989             input.name = this.name;
39990         }
39991
39992         if (this.disabled) {
39993             input.disabled = true;
39994         }
39995
39996         var clg = 12 - this.inputlg;
39997         var cmd = 12 - this.inputmd;
39998         var csm = 12 - this.inputsm;
39999         var cxs = 12 - this.inputxs;
40000         
40001         var container = {
40002             tag : 'div',
40003             cls : 'row roo-money-field',
40004             cn : [
40005                 {
40006                     tag : 'div',
40007                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40008                     cn : [
40009                         {
40010                             tag : 'div',
40011                             cls: 'roo-select2-container input-group',
40012                             cn: [
40013                                 {
40014                                     tag : 'input',
40015                                     cls : 'form-control roo-money-currency-input',
40016                                     autocomplete: 'new-password',
40017                                     readOnly : 1,
40018                                     name : this.currencyName
40019                                 },
40020                                 {
40021                                     tag :'span',
40022                                     cls : 'input-group-addon',
40023                                     cn : [
40024                                         {
40025                                             tag: 'span',
40026                                             cls: 'caret'
40027                                         }
40028                                     ]
40029                                 }
40030                             ]
40031                         }
40032                     ]
40033                 },
40034                 {
40035                     tag : 'div',
40036                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40037                     cn : [
40038                         {
40039                             tag: 'div',
40040                             cls: this.hasFeedback ? 'has-feedback' : '',
40041                             cn: [
40042                                 input
40043                             ]
40044                         }
40045                     ]
40046                 }
40047             ]
40048             
40049         };
40050         
40051         if (this.fieldLabel.length) {
40052             var indicator = {
40053                 tag: 'i',
40054                 tooltip: 'This field is required'
40055             };
40056
40057             var label = {
40058                 tag: 'label',
40059                 'for':  id,
40060                 cls: 'control-label',
40061                 cn: []
40062             };
40063
40064             var label_text = {
40065                 tag: 'span',
40066                 html: this.fieldLabel
40067             };
40068
40069             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40070             label.cn = [
40071                 indicator,
40072                 label_text
40073             ];
40074
40075             if(this.indicatorpos == 'right') {
40076                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40077                 label.cn = [
40078                     label_text,
40079                     indicator
40080                 ];
40081             }
40082
40083             if(align == 'left') {
40084                 container = {
40085                     tag: 'div',
40086                     cn: [
40087                         container
40088                     ]
40089                 };
40090
40091                 if(this.labelWidth > 12){
40092                     label.style = "width: " + this.labelWidth + 'px';
40093                 }
40094                 if(this.labelWidth < 13 && this.labelmd == 0){
40095                     this.labelmd = this.labelWidth;
40096                 }
40097                 if(this.labellg > 0){
40098                     label.cls += ' col-lg-' + this.labellg;
40099                     input.cls += ' col-lg-' + (12 - this.labellg);
40100                 }
40101                 if(this.labelmd > 0){
40102                     label.cls += ' col-md-' + this.labelmd;
40103                     container.cls += ' col-md-' + (12 - this.labelmd);
40104                 }
40105                 if(this.labelsm > 0){
40106                     label.cls += ' col-sm-' + this.labelsm;
40107                     container.cls += ' col-sm-' + (12 - this.labelsm);
40108                 }
40109                 if(this.labelxs > 0){
40110                     label.cls += ' col-xs-' + this.labelxs;
40111                     container.cls += ' col-xs-' + (12 - this.labelxs);
40112                 }
40113             }
40114         }
40115
40116         cfg.cn = [
40117             label,
40118             container
40119         ];
40120
40121         var settings = this;
40122
40123         ['xs','sm','md','lg'].map(function(size){
40124             if (settings[size]) {
40125                 cfg.cls += ' col-' + size + '-' + settings[size];
40126             }
40127         });
40128         
40129         return cfg;
40130         
40131     },
40132     
40133     initEvents : function()
40134     {
40135         this.indicator = this.indicatorEl();
40136         
40137         this.initCurrencyEvent();
40138         
40139         this.initNumberEvent();
40140         
40141     },
40142     
40143     initCurrencyEvent : function()
40144     {
40145         if (!this.store) {
40146             throw "can not find store for combo";
40147         }
40148         
40149         this.store = Roo.factory(this.store, Roo.data);
40150         this.store.parent = this;
40151         
40152         this.createList();
40153         
40154         this.triggerEl = this.el.select('.input-group-addon', true).first();
40155         
40156         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40157         
40158         var _this = this;
40159         
40160         (function(){
40161             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40162             _this.list.setWidth(lw);
40163         }).defer(100);
40164         
40165         this.list.on('mouseover', this.onViewOver, this);
40166         this.list.on('mousemove', this.onViewMove, this);
40167         this.list.on('scroll', this.onViewScroll, this);
40168         
40169         if(!this.tpl){
40170             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40171         }
40172         
40173         this.view = new Roo.View(this.list, this.tpl, {
40174             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40175         });
40176         
40177         this.view.on('click', this.onViewClick, this);
40178         
40179         this.store.on('beforeload', this.onBeforeLoad, this);
40180         this.store.on('load', this.onLoad, this);
40181         this.store.on('loadexception', this.onLoadException, this);
40182         
40183         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40184             "up" : function(e){
40185                 this.inKeyMode = true;
40186                 this.selectPrev();
40187             },
40188
40189             "down" : function(e){
40190                 if(!this.isExpanded()){
40191                     this.onTriggerClick();
40192                 }else{
40193                     this.inKeyMode = true;
40194                     this.selectNext();
40195                 }
40196             },
40197
40198             "enter" : function(e){
40199                 this.collapse();
40200                 
40201                 if(this.fireEvent("specialkey", this, e)){
40202                     this.onViewClick(false);
40203                 }
40204                 
40205                 return true;
40206             },
40207
40208             "esc" : function(e){
40209                 this.collapse();
40210             },
40211
40212             "tab" : function(e){
40213                 this.collapse();
40214                 
40215                 if(this.fireEvent("specialkey", this, e)){
40216                     this.onViewClick(false);
40217                 }
40218                 
40219                 return true;
40220             },
40221
40222             scope : this,
40223
40224             doRelay : function(foo, bar, hname){
40225                 if(hname == 'down' || this.scope.isExpanded()){
40226                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40227                 }
40228                 return true;
40229             },
40230
40231             forceKeyDown: true
40232         });
40233         
40234         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40235         
40236     },
40237     
40238     initNumberEvent : function(e)
40239     {
40240         this.inputEl().on("keydown" , this.fireKey,  this);
40241         this.inputEl().on("focus", this.onFocus,  this);
40242         this.inputEl().on("blur", this.onBlur,  this);
40243         
40244         this.inputEl().relayEvent('keyup', this);
40245         
40246         if(this.indicator){
40247             this.indicator.addClass('invisible');
40248         }
40249  
40250         this.originalValue = this.getValue();
40251         
40252         if(this.validationEvent == 'keyup'){
40253             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40254             this.inputEl().on('keyup', this.filterValidation, this);
40255         }
40256         else if(this.validationEvent !== false){
40257             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40258         }
40259         
40260         if(this.selectOnFocus){
40261             this.on("focus", this.preFocus, this);
40262             
40263         }
40264         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40265             this.inputEl().on("keypress", this.filterKeys, this);
40266         } else {
40267             this.inputEl().relayEvent('keypress', this);
40268         }
40269         
40270         var allowed = "0123456789";
40271         
40272         if(this.allowDecimals){
40273             allowed += this.decimalSeparator;
40274         }
40275         
40276         if(this.allowNegative){
40277             allowed += "-";
40278         }
40279         
40280         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40281         
40282         var keyPress = function(e){
40283             
40284             var k = e.getKey();
40285             
40286             var c = e.getCharCode();
40287             
40288             if(
40289                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40290                     allowed.indexOf(String.fromCharCode(c)) === -1
40291             ){
40292                 e.stopEvent();
40293                 return;
40294             }
40295             
40296             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40297                 return;
40298             }
40299             
40300             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40301                 e.stopEvent();
40302             }
40303         };
40304         
40305         this.inputEl().on("keypress", keyPress, this);
40306         
40307     },
40308     
40309     onTriggerClick : function(e)
40310     {   
40311         if(this.disabled){
40312             return;
40313         }
40314         
40315         this.page = 0;
40316         this.loadNext = false;
40317         
40318         if(this.isExpanded()){
40319             this.collapse();
40320             return;
40321         }
40322         
40323         this.hasFocus = true;
40324         
40325         if(this.triggerAction == 'all') {
40326             this.doQuery(this.allQuery, true);
40327             return;
40328         }
40329         
40330         this.doQuery(this.getRawValue());
40331     },
40332     
40333     getCurrency : function()
40334     {   
40335         var v = this.currencyEl().getValue();
40336         
40337         return v;
40338     },
40339     
40340     restrictHeight : function()
40341     {
40342         this.list.alignTo(this.currencyEl(), this.listAlign);
40343         this.list.alignTo(this.currencyEl(), this.listAlign);
40344     },
40345     
40346     onViewClick : function(view, doFocus, el, e)
40347     {
40348         var index = this.view.getSelectedIndexes()[0];
40349         
40350         var r = this.store.getAt(index);
40351         
40352         if(r){
40353             this.onSelect(r, index);
40354         }
40355     },
40356     
40357     onSelect : function(record, index){
40358         
40359         if(this.fireEvent('beforeselect', this, record, index) !== false){
40360         
40361             this.setFromCurrencyData(index > -1 ? record.data : false);
40362             
40363             this.collapse();
40364             
40365             this.fireEvent('select', this, record, index);
40366         }
40367     },
40368     
40369     setFromCurrencyData : function(o)
40370     {
40371         var currency = '';
40372         
40373         this.lastCurrency = o;
40374         
40375         if (this.currencyField) {
40376             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40377         } else {
40378             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40379         }
40380         
40381         this.lastSelectionText = currency;
40382         
40383         this.setCurrency(currency);
40384     },
40385     
40386     setFromData : function(o)
40387     {
40388         var c = {};
40389         
40390         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40391         
40392         this.setFromCurrencyData(c);
40393         
40394         var value = '';
40395         
40396         if (this.name) {
40397             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40398         } else {
40399             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40400         }
40401         
40402         this.setValue(value);
40403         
40404     },
40405     
40406     setCurrency : function(v)
40407     {   
40408         this.currencyValue = v;
40409         
40410         if(this.rendered){
40411             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40412             this.validate();
40413         }
40414     },
40415     
40416     setValue : function(v)
40417     {
40418         v = this.fixPrecision(v);
40419         
40420         v = String(v).replace(".", this.decimalSeparator);
40421         
40422         this.value = v;
40423         
40424         if(this.rendered){
40425             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40426             this.validate();
40427         }
40428     },
40429     
40430     getRawValue : function()
40431     {
40432         var v = this.inputEl().getValue();
40433         
40434         return v;
40435     },
40436     
40437     getValue : function()
40438     {
40439         return this.fixPrecision(this.parseValue(this.getRawValue()));
40440     },
40441     
40442     parseValue : function(value)
40443     {
40444         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40445         return isNaN(value) ? '' : value;
40446     },
40447     
40448     fixPrecision : function(value)
40449     {
40450         var nan = isNaN(value);
40451         
40452         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40453             return nan ? '' : value;
40454         }
40455         
40456         return parseFloat(value).toFixed(this.decimalPrecision);
40457     },
40458     
40459     decimalPrecisionFcn : function(v)
40460     {
40461         return Math.floor(v);
40462     },
40463     
40464     validateValue : function(value)
40465     {
40466         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40467             return false;
40468         }
40469         
40470         var num = this.parseValue(value);
40471         
40472         if(isNaN(num)){
40473             this.markInvalid(String.format(this.nanText, value));
40474             return false;
40475         }
40476         
40477         if(num < this.minValue){
40478             this.markInvalid(String.format(this.minText, this.minValue));
40479             return false;
40480         }
40481         
40482         if(num > this.maxValue){
40483             this.markInvalid(String.format(this.maxText, this.maxValue));
40484             return false;
40485         }
40486         
40487         return true;
40488     },
40489     
40490     validate : function()
40491     {
40492         if(this.disabled || this.allowBlank){
40493             this.markValid();
40494             return true;
40495         }
40496         
40497         var currency = this.getCurrency();
40498         
40499         if(this.validateValue(this.getRawValue()) && currency.length){
40500             this.markValid();
40501             return true;
40502         }
40503         
40504         this.markInvalid();
40505         return false;
40506     },
40507     
40508     getName: function()
40509     {
40510         return this.name;
40511     },
40512     
40513     beforeBlur : function()
40514     {
40515         if(!this.castInt){
40516             return;
40517         }
40518         
40519         var v = this.parseValue(this.getRawValue());
40520         
40521         if(v){
40522             this.setValue(v);
40523         }
40524     },
40525     
40526     onBlur : function()
40527     {
40528         this.beforeBlur();
40529         
40530         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40531             //this.el.removeClass(this.focusClass);
40532         }
40533         
40534         this.hasFocus = false;
40535         
40536         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40537             this.validate();
40538         }
40539         
40540         var v = this.getValue();
40541         
40542         if(String(v) !== String(this.startValue)){
40543             this.fireEvent('change', this, v, this.startValue);
40544         }
40545         
40546         this.fireEvent("blur", this);
40547     },
40548     
40549     inputEl : function()
40550     {
40551         return this.el.select('.roo-money-amount-input', true).first();
40552     },
40553     
40554     currencyEl : function()
40555     {
40556         return this.el.select('.roo-money-currency-input', true).first();
40557     }
40558     
40559 });