Roo/bootstrap/Form.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                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  *
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  *
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393
394     config = config || {};
395
396     Roo.bootstrap.Body.superclass.constructor.call(this, config);
397     this.el = Roo.get(config.el ? config.el : document.body );
398     if (this.cls && this.cls.length) {
399         Roo.get(document.body).addClass(this.cls);
400     }
401 };
402
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
404
405     is_body : true,// just to make sure it's constructed?
406
407         autoCreate : {
408         cls: 'container'
409     },
410     onRender : function(ct, position)
411     {
412        /* Roo.log("Roo.bootstrap.Body - onRender");
413         if (this.cls && this.cls.length) {
414             Roo.get(document.body).addClass(this.cls);
415         }
416         // style??? xttr???
417         */
418     }
419
420
421
422
423 });
424 /*
425  * - LGPL
426  *
427  * button group
428  * 
429  */
430
431
432 /**
433  * @class Roo.bootstrap.ButtonGroup
434  * @extends Roo.bootstrap.Component
435  * Bootstrap ButtonGroup class
436  * @cfg {String} size lg | sm | xs (default empty normal)
437  * @cfg {String} align vertical | justified  (default none)
438  * @cfg {String} direction up | down (default down)
439  * @cfg {Boolean} toolbar false | true
440  * @cfg {Boolean} btn true | false
441  * 
442  * 
443  * @constructor
444  * Create a new Input
445  * @param {Object} config The config object
446  */
447
448 Roo.bootstrap.ButtonGroup = function(config){
449     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 };
451
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
453     
454     size: '',
455     align: '',
456     direction: '',
457     toolbar: false,
458     btn: true,
459
460     getAutoCreate : function(){
461         var cfg = {
462             cls: 'btn-group',
463             html : null
464         };
465         
466         cfg.html = this.html || cfg.html;
467         
468         if (this.toolbar) {
469             cfg = {
470                 cls: 'btn-toolbar',
471                 html: null
472             };
473             
474             return cfg;
475         }
476         
477         if (['vertical','justified'].indexOf(this.align)!==-1) {
478             cfg.cls = 'btn-group-' + this.align;
479             
480             if (this.align == 'justified') {
481                 console.log(this.items);
482             }
483         }
484         
485         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486             cfg.cls += ' btn-group-' + this.size;
487         }
488         
489         if (this.direction == 'up') {
490             cfg.cls += ' dropup' ;
491         }
492         
493         return cfg;
494     }
495    
496 });
497
498  /*
499  * - LGPL
500  *
501  * button
502  * 
503  */
504
505 /**
506  * @class Roo.bootstrap.Button
507  * @extends Roo.bootstrap.Component
508  * Bootstrap Button class
509  * @cfg {String} html The button content
510  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
511  * @cfg {String} size ( lg | sm | xs)
512  * @cfg {String} tag ( a | input | submit)
513  * @cfg {String} href empty or href
514  * @cfg {Boolean} disabled default false;
515  * @cfg {Boolean} isClose default false;
516  * @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)
517  * @cfg {String} badge text for badge
518  * @cfg {String} theme default 
519  * @cfg {Boolean} inverse 
520  * @cfg {Boolean} toggle 
521  * @cfg {String} ontext text for on toggle state
522  * @cfg {String} offtext text for off toggle state
523  * @cfg {Boolean} defaulton 
524  * @cfg {Boolean} preventDefault  default true
525  * @cfg {Boolean} removeClass remove the standard class..
526  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
527  * 
528  * @constructor
529  * Create a new button
530  * @param {Object} config The config object
531  */
532
533
534 Roo.bootstrap.Button = function(config){
535     Roo.bootstrap.Button.superclass.constructor.call(this, config);
536     this.weightClass = ["btn-default", 
537                        "btn-primary", 
538                        "btn-success", 
539                        "btn-info", 
540                        "btn-warning",
541                        "btn-danger",
542                        "btn-link"
543                       ],  
544     this.addEvents({
545         // raw events
546         /**
547          * @event click
548          * When a butotn is pressed
549          * @param {Roo.bootstrap.Button} this
550          * @param {Roo.EventObject} e
551          */
552         "click" : true,
553          /**
554          * @event toggle
555          * After the button has been toggles
556          * @param {Roo.EventObject} e
557          * @param {boolean} pressed (also available as button.pressed)
558          */
559         "toggle" : true
560     });
561 };
562
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
564     html: false,
565     active: false,
566     weight: '',
567     size: '',
568     tag: 'button',
569     href: '',
570     disabled: false,
571     isClose: false,
572     glyphicon: '',
573     badge: '',
574     theme: 'default',
575     inverse: false,
576     
577     toggle: false,
578     ontext: 'ON',
579     offtext: 'OFF',
580     defaulton: true,
581     preventDefault: true,
582     removeClass: false,
583     name: false,
584     target: false,
585     
586     
587     pressed : null,
588      
589     
590     getAutoCreate : function(){
591         
592         var cfg = {
593             tag : 'button',
594             cls : 'roo-button',
595             html: ''
596         };
597         
598         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
600             this.tag = 'button';
601         } else {
602             cfg.tag = this.tag;
603         }
604         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
605         
606         if (this.toggle == true) {
607             cfg={
608                 tag: 'div',
609                 cls: 'slider-frame roo-button',
610                 cn: [
611                     {
612                         tag: 'span',
613                         'data-on-text':'ON',
614                         'data-off-text':'OFF',
615                         cls: 'slider-button',
616                         html: this.offtext
617                     }
618                 ]
619             };
620             
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 cfg.cls += ' '+this.weight;
623             }
624             
625             return cfg;
626         }
627         
628         if (this.isClose) {
629             cfg.cls += ' close';
630             
631             cfg["aria-hidden"] = true;
632             
633             cfg.html = "&times;";
634             
635             return cfg;
636         }
637         
638          
639         if (this.theme==='default') {
640             cfg.cls = 'btn roo-button';
641             
642             //if (this.parentType != 'Navbar') {
643             this.weight = this.weight.length ?  this.weight : 'default';
644             //}
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' btn-' + this.weight;
648             }
649         } else if (this.theme==='glow') {
650             
651             cfg.tag = 'a';
652             cfg.cls = 'btn-glow roo-button';
653             
654             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
655                 
656                 cfg.cls += ' ' + this.weight;
657             }
658         }
659    
660         
661         if (this.inverse) {
662             this.cls += ' inverse';
663         }
664         
665         
666         if (this.active) {
667             cfg.cls += ' active';
668         }
669         
670         if (this.disabled) {
671             cfg.disabled = 'disabled';
672         }
673         
674         if (this.items) {
675             Roo.log('changing to ul' );
676             cfg.tag = 'ul';
677             this.glyphicon = 'caret';
678         }
679         
680         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
681          
682         //gsRoo.log(this.parentType);
683         if (this.parentType === 'Navbar' && !this.parent().bar) {
684             Roo.log('changing to li?');
685             
686             cfg.tag = 'li';
687             
688             cfg.cls = '';
689             cfg.cn =  [{
690                 tag : 'a',
691                 cls : 'roo-button',
692                 html : this.html,
693                 href : this.href || '#'
694             }];
695             if (this.menu) {
696                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
697                 cfg.cls += ' dropdown';
698             }   
699             
700             delete cfg.html;
701             
702         }
703         
704        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
705         
706         if (this.glyphicon) {
707             cfg.html = ' ' + cfg.html;
708             
709             cfg.cn = [
710                 {
711                     tag: 'span',
712                     cls: 'glyphicon glyphicon-' + this.glyphicon
713                 }
714             ];
715         }
716         
717         if (this.badge) {
718             cfg.html += ' ';
719             
720             cfg.tag = 'a';
721             
722 //            cfg.cls='btn roo-button';
723             
724             cfg.href=this.href;
725             
726             var value = cfg.html;
727             
728             if(this.glyphicon){
729                 value = {
730                             tag: 'span',
731                             cls: 'glyphicon glyphicon-' + this.glyphicon,
732                             html: this.html
733                         };
734                 
735             }
736             
737             cfg.cn = [
738                 value,
739                 {
740                     tag: 'span',
741                     cls: 'badge',
742                     html: this.badge
743                 }
744             ];
745             
746             cfg.html='';
747         }
748         
749         if (this.menu) {
750             cfg.cls += ' dropdown';
751             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
752         }
753         
754         if (cfg.tag !== 'a' && this.href !== '') {
755             throw "Tag must be a to set href.";
756         } else if (this.href.length > 0) {
757             cfg.href = this.href;
758         }
759         
760         if(this.removeClass){
761             cfg.cls = '';
762         }
763         
764         if(this.target){
765             cfg.target = this.target;
766         }
767         
768         return cfg;
769     },
770     initEvents: function() {
771        // Roo.log('init events?');
772 //        Roo.log(this.el.dom);
773         // add the menu...
774         
775         if (typeof (this.menu) != 'undefined') {
776             this.menu.parentType = this.xtype;
777             this.menu.triggerEl = this.el;
778             this.addxtype(Roo.apply({}, this.menu));
779         }
780
781
782        if (this.el.hasClass('roo-button')) {
783             this.el.on('click', this.onClick, this);
784        } else {
785             this.el.select('.roo-button').on('click', this.onClick, this);
786        }
787        
788        if(this.removeClass){
789            this.el.on('click', this.onClick, this);
790        }
791        
792        this.el.enableDisplayMode();
793         
794     },
795     onClick : function(e)
796     {
797         if (this.disabled) {
798             return;
799         }
800         
801         
802         Roo.log('button on click ');
803         if(this.preventDefault){
804             e.preventDefault();
805         }
806         if (this.pressed === true || this.pressed === false) {
807             this.pressed = !this.pressed;
808             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809             this.fireEvent('toggle', this, e, this.pressed);
810         }
811         
812         
813         this.fireEvent('click', this, e);
814     },
815     
816     /**
817      * Enables this button
818      */
819     enable : function()
820     {
821         this.disabled = false;
822         this.el.removeClass('disabled');
823     },
824     
825     /**
826      * Disable this button
827      */
828     disable : function()
829     {
830         this.disabled = true;
831         this.el.addClass('disabled');
832     },
833      /**
834      * sets the active state on/off, 
835      * @param {Boolean} state (optional) Force a particular state
836      */
837     setActive : function(v) {
838         
839         this.el[v ? 'addClass' : 'removeClass']('active');
840     },
841      /**
842      * toggles the current active state 
843      */
844     toggleActive : function()
845     {
846        var active = this.el.hasClass('active');
847        this.setActive(!active);
848        
849         
850     },
851     setText : function(str)
852     {
853         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
854     },
855     getText : function()
856     {
857         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858     },
859     hide: function() {
860        
861      
862         this.el.hide();   
863     },
864     show: function() {
865        
866         this.el.show();   
867     },
868     setWeight : function(str)
869     {
870           this.el.removeClass(this.weightClass);
871         this.el.addClass('btn-' + str);        
872     }
873     
874     
875 });
876
877  /*
878  * - LGPL
879  *
880  * column
881  * 
882  */
883
884 /**
885  * @class Roo.bootstrap.Column
886  * @extends Roo.bootstrap.Component
887  * Bootstrap Column class
888  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
896  *
897  * 
898  * @cfg {Boolean} hidden (true|false) hide the element
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {Number} fasize (1|2|....) font awsome size
902
903  * @cfg {String} icon (info-sign|check|...) glyphicon name
904
905  * @cfg {String} html content of column.
906  * 
907  * @constructor
908  * Create a new Column
909  * @param {Object} config The config object
910  */
911
912 Roo.bootstrap.Column = function(config){
913     Roo.bootstrap.Column.superclass.constructor.call(this, config);
914 };
915
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
917     
918     xs: false,
919     sm: false,
920     md: false,
921     lg: false,
922     xsoff: false,
923     smoff: false,
924     mdoff: false,
925     lgoff: false,
926     html: '',
927     offset: 0,
928     alert: false,
929     fa: false,
930     icon : false,
931     hidden : false,
932     fasize : 1,
933     
934     getAutoCreate : function(){
935         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
936         
937         cfg = {
938             tag: 'div',
939             cls: 'column'
940         };
941         
942         var settings=this;
943         ['xs','sm','md','lg'].map(function(size){
944             //Roo.log( size + ':' + settings[size]);
945             
946             if (settings[size+'off'] !== false) {
947                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
948             }
949             
950             if (settings[size] === false) {
951                 return;
952             }
953             
954             if (!settings[size]) { // 0 = hidden
955                 cfg.cls += ' hidden-' + size;
956                 return;
957             }
958             cfg.cls += ' col-' + size + '-' + settings[size];
959             
960         });
961         
962         if (this.hidden) {
963             cfg.cls += ' hidden';
964         }
965         
966         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967             cfg.cls +=' alert alert-' + this.alert;
968         }
969         
970         
971         if (this.html.length) {
972             cfg.html = this.html;
973         }
974         if (this.fa) {
975             var fasize = '';
976             if (this.fasize > 1) {
977                 fasize = ' fa-' + this.fasize + 'x';
978             }
979             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
980             
981             
982         }
983         if (this.icon) {
984             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
985         }
986         
987         return cfg;
988     }
989    
990 });
991
992  
993
994  /*
995  * - LGPL
996  *
997  * page container.
998  * 
999  */
1000
1001
1002 /**
1003  * @class Roo.bootstrap.Container
1004  * @extends Roo.bootstrap.Component
1005  * Bootstrap Container class
1006  * @cfg {Boolean} jumbotron is it a jumbotron element
1007  * @cfg {String} html content of element
1008  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1010  * @cfg {String} header content of header (for panel)
1011  * @cfg {String} footer content of footer (for panel)
1012  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013  * @cfg {String} tag (header|aside|section) type of HTML tag.
1014  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015  * @cfg {String} fa font awesome icon
1016  * @cfg {String} icon (info-sign|check|...) glyphicon name
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {Boolean} expandable (true|false) default false
1019  * @cfg {Boolean} expanded (true|false) default true
1020  * @cfg {String} rheader contet on the right of header
1021  * @cfg {Boolean} clickable (true|false) default false
1022
1023  *     
1024  * @constructor
1025  * Create a new Container
1026  * @param {Object} config The config object
1027  */
1028
1029 Roo.bootstrap.Container = function(config){
1030     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034          /**
1035          * @event expand
1036          * After the panel has been expand
1037          * 
1038          * @param {Roo.bootstrap.Container} this
1039          */
1040         "expand" : true,
1041         /**
1042          * @event collapse
1043          * After the panel has been collapsed
1044          * 
1045          * @param {Roo.bootstrap.Container} this
1046          */
1047         "collapse" : true,
1048         /**
1049          * @event click
1050          * When a element is chick
1051          * @param {Roo.bootstrap.Container} this
1052          * @param {Roo.EventObject} e
1053          */
1054         "click" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1059     
1060     jumbotron : false,
1061     well: '',
1062     panel : '',
1063     header: '',
1064     footer : '',
1065     sticky: '',
1066     tag : false,
1067     alert : false,
1068     fa: false,
1069     icon : false,
1070     expandable : false,
1071     rheader : '',
1072     expanded : true,
1073     clickable: false,
1074   
1075      
1076     getChildContainer : function() {
1077         
1078         if(!this.el){
1079             return false;
1080         }
1081         
1082         if (this.panel.length) {
1083             return this.el.select('.panel-body',true).first();
1084         }
1085         
1086         return this.el;
1087     },
1088     
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : this.tag || 'div',
1094             html : '',
1095             cls : ''
1096         };
1097         if (this.jumbotron) {
1098             cfg.cls = 'jumbotron';
1099         }
1100         
1101         
1102         
1103         // - this is applied by the parent..
1104         //if (this.cls) {
1105         //    cfg.cls = this.cls + '';
1106         //}
1107         
1108         if (this.sticky.length) {
1109             
1110             var bd = Roo.get(document.body);
1111             if (!bd.hasClass('bootstrap-sticky')) {
1112                 bd.addClass('bootstrap-sticky');
1113                 Roo.select('html',true).setStyle('height', '100%');
1114             }
1115              
1116             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1117         }
1118         
1119         
1120         if (this.well.length) {
1121             switch (this.well) {
1122                 case 'lg':
1123                 case 'sm':
1124                     cfg.cls +=' well well-' +this.well;
1125                     break;
1126                 default:
1127                     cfg.cls +=' well';
1128                     break;
1129             }
1130         }
1131         
1132         if (this.hidden) {
1133             cfg.cls += ' hidden';
1134         }
1135         
1136         
1137         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138             cfg.cls +=' alert alert-' + this.alert;
1139         }
1140         
1141         var body = cfg;
1142         
1143         if (this.panel.length) {
1144             cfg.cls += ' panel panel-' + this.panel;
1145             cfg.cn = [];
1146             if (this.header.length) {
1147                 
1148                 var h = [];
1149                 
1150                 if(this.expandable){
1151                     
1152                     cfg.cls = cfg.cls + ' expandable';
1153                     
1154                     h.push({
1155                         tag: 'i',
1156                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1157                     });
1158                     
1159                 }
1160                 
1161                 h.push(
1162                     {
1163                         tag: 'span',
1164                         cls : 'panel-title',
1165                         html : (this.expandable ? '&nbsp;' : '') + this.header
1166                     },
1167                     {
1168                         tag: 'span',
1169                         cls: 'panel-header-right',
1170                         html: this.rheader
1171                     }
1172                 );
1173                 
1174                 cfg.cn.push({
1175                     cls : 'panel-heading',
1176                     style : this.expandable ? 'cursor: pointer' : '',
1177                     cn : h
1178                 });
1179                 
1180             }
1181             
1182             body = false;
1183             cfg.cn.push({
1184                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1185                 html : this.html
1186             });
1187             
1188             
1189             if (this.footer.length) {
1190                 cfg.cn.push({
1191                     cls : 'panel-footer',
1192                     html : this.footer
1193                     
1194                 });
1195             }
1196             
1197         }
1198         
1199         if (body) {
1200             body.html = this.html || cfg.html;
1201             // prefix with the icons..
1202             if (this.fa) {
1203                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1204             }
1205             if (this.icon) {
1206                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1207             }
1208             
1209             
1210         }
1211         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212             cfg.cls =  'container';
1213         }
1214         
1215         return cfg;
1216     },
1217     
1218     initEvents: function() 
1219     {
1220         if(this.expandable){
1221             var headerEl = this.headerEl();
1222         
1223             if(headerEl){
1224                 headerEl.on('click', this.onToggleClick, this);
1225             }
1226         }
1227         
1228         if(this.clickable){
1229             this.el.on('click', this.onClick, this);
1230         }
1231         
1232     },
1233     
1234     onToggleClick : function()
1235     {
1236         var headerEl = this.headerEl();
1237         
1238         if(!headerEl){
1239             return;
1240         }
1241         
1242         if(this.expanded){
1243             this.collapse();
1244             return;
1245         }
1246         
1247         this.expand();
1248     },
1249     
1250     expand : function()
1251     {
1252         if(this.fireEvent('expand', this)) {
1253             
1254             this.expanded = true;
1255             
1256             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1257             
1258             this.el.select('.panel-body',true).first().removeClass('hide');
1259             
1260             var toggleEl = this.toggleEl();
1261
1262             if(!toggleEl){
1263                 return;
1264             }
1265
1266             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1267         }
1268         
1269     },
1270     
1271     collapse : function()
1272     {
1273         if(this.fireEvent('collapse', this)) {
1274             
1275             this.expanded = false;
1276             
1277             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278             this.el.select('.panel-body',true).first().addClass('hide');
1279         
1280             var toggleEl = this.toggleEl();
1281
1282             if(!toggleEl){
1283                 return;
1284             }
1285
1286             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1287         }
1288     },
1289     
1290     toggleEl : function()
1291     {
1292         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1293             return;
1294         }
1295         
1296         return this.el.select('.panel-heading .fa',true).first();
1297     },
1298     
1299     headerEl : function()
1300     {
1301         if(!this.el || !this.panel.length || !this.header.length){
1302             return;
1303         }
1304         
1305         return this.el.select('.panel-heading',true).first()
1306     },
1307     
1308     bodyEl : function()
1309     {
1310         if(!this.el || !this.panel.length){
1311             return;
1312         }
1313         
1314         return this.el.select('.panel-body',true).first()
1315     },
1316     
1317     titleEl : function()
1318     {
1319         if(!this.el || !this.panel.length || !this.header.length){
1320             return;
1321         }
1322         
1323         return this.el.select('.panel-title',true).first();
1324     },
1325     
1326     setTitle : function(v)
1327     {
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return;
1332         }
1333         
1334         titleEl.dom.innerHTML = v;
1335     },
1336     
1337     getTitle : function()
1338     {
1339         
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return '';
1344         }
1345         
1346         return titleEl.dom.innerHTML;
1347     },
1348     
1349     setRightTitle : function(v)
1350     {
1351         var t = this.el.select('.panel-header-right',true).first();
1352         
1353         if(!t){
1354             return;
1355         }
1356         
1357         t.dom.innerHTML = v;
1358     },
1359     
1360     onClick : function(e)
1361     {
1362         e.preventDefault();
1363         
1364         this.fireEvent('click', this, e);
1365     }
1366    
1367 });
1368
1369  /*
1370  * - LGPL
1371  *
1372  * image
1373  * 
1374  */
1375
1376
1377 /**
1378  * @class Roo.bootstrap.Img
1379  * @extends Roo.bootstrap.Component
1380  * Bootstrap Img class
1381  * @cfg {Boolean} imgResponsive false | true
1382  * @cfg {String} border rounded | circle | thumbnail
1383  * @cfg {String} src image source
1384  * @cfg {String} alt image alternative text
1385  * @cfg {String} href a tag href
1386  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387  * @cfg {String} xsUrl xs image source
1388  * @cfg {String} smUrl sm image source
1389  * @cfg {String} mdUrl md image source
1390  * @cfg {String} lgUrl lg image source
1391  * 
1392  * @constructor
1393  * Create a new Input
1394  * @param {Object} config The config object
1395  */
1396
1397 Roo.bootstrap.Img = function(config){
1398     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1399     
1400     this.addEvents({
1401         // img events
1402         /**
1403          * @event click
1404          * The img click event for the img.
1405          * @param {Roo.EventObject} e
1406          */
1407         "click" : true
1408     });
1409 };
1410
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1412     
1413     imgResponsive: true,
1414     border: '',
1415     src: 'about:blank',
1416     href: false,
1417     target: false,
1418     xsUrl: '',
1419     smUrl: '',
1420     mdUrl: '',
1421     lgUrl: '',
1422
1423     getAutoCreate : function()
1424     {   
1425         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426             return this.createSingleImg();
1427         }
1428         
1429         var cfg = {
1430             tag: 'div',
1431             cls: 'roo-image-responsive-group',
1432             cn: []
1433         };
1434         var _this = this;
1435         
1436         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1437             
1438             if(!_this[size + 'Url']){
1439                 return;
1440             }
1441             
1442             var img = {
1443                 tag: 'img',
1444                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445                 html: _this.html || cfg.html,
1446                 src: _this[size + 'Url']
1447             };
1448             
1449             img.cls += ' roo-image-responsive-' + size;
1450             
1451             var s = ['xs', 'sm', 'md', 'lg'];
1452             
1453             s.splice(s.indexOf(size), 1);
1454             
1455             Roo.each(s, function(ss){
1456                 img.cls += ' hidden-' + ss;
1457             });
1458             
1459             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460                 cfg.cls += ' img-' + _this.border;
1461             }
1462             
1463             if(_this.alt){
1464                 cfg.alt = _this.alt;
1465             }
1466             
1467             if(_this.href){
1468                 var a = {
1469                     tag: 'a',
1470                     href: _this.href,
1471                     cn: [
1472                         img
1473                     ]
1474                 };
1475
1476                 if(this.target){
1477                     a.target = _this.target;
1478                 }
1479             }
1480             
1481             cfg.cn.push((_this.href) ? a : img);
1482             
1483         });
1484         
1485         return cfg;
1486     },
1487     
1488     createSingleImg : function()
1489     {
1490         var cfg = {
1491             tag: 'img',
1492             cls: (this.imgResponsive) ? 'img-responsive' : '',
1493             html : null,
1494             src : 'about:blank'  // just incase src get's set to undefined?!?
1495         };
1496         
1497         cfg.html = this.html || cfg.html;
1498         
1499         cfg.src = this.src || cfg.src;
1500         
1501         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502             cfg.cls += ' img-' + this.border;
1503         }
1504         
1505         if(this.alt){
1506             cfg.alt = this.alt;
1507         }
1508         
1509         if(this.href){
1510             var a = {
1511                 tag: 'a',
1512                 href: this.href,
1513                 cn: [
1514                     cfg
1515                 ]
1516             };
1517             
1518             if(this.target){
1519                 a.target = this.target;
1520             }
1521             
1522         }
1523         
1524         return (this.href) ? a : cfg;
1525     },
1526     
1527     initEvents: function() 
1528     {
1529         if(!this.href){
1530             this.el.on('click', this.onClick, this);
1531         }
1532         
1533     },
1534     
1535     onClick : function(e)
1536     {
1537         Roo.log('img onclick');
1538         this.fireEvent('click', this, e);
1539     },
1540     /**
1541      * Sets the url of the image - used to update it
1542      * @param {String} url the url of the image
1543      */
1544     
1545     setSrc : function(url)
1546     {
1547         this.src =  url;
1548         
1549         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550             this.el.dom.src =  url;
1551             return;
1552         }
1553         
1554         this.el.select('img', true).first().dom.src =  url;
1555     }
1556     
1557     
1558    
1559 });
1560
1561  /*
1562  * - LGPL
1563  *
1564  * image
1565  * 
1566  */
1567
1568
1569 /**
1570  * @class Roo.bootstrap.Link
1571  * @extends Roo.bootstrap.Component
1572  * Bootstrap Link Class
1573  * @cfg {String} alt image alternative text
1574  * @cfg {String} href a tag href
1575  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576  * @cfg {String} html the content of the link.
1577  * @cfg {String} anchor name for the anchor link
1578  * @cfg {String} fa - favicon
1579
1580  * @cfg {Boolean} preventDefault (true | false) default false
1581
1582  * 
1583  * @constructor
1584  * Create a new Input
1585  * @param {Object} config The config object
1586  */
1587
1588 Roo.bootstrap.Link = function(config){
1589     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1590     
1591     this.addEvents({
1592         // img events
1593         /**
1594          * @event click
1595          * The img click event for the img.
1596          * @param {Roo.EventObject} e
1597          */
1598         "click" : true
1599     });
1600 };
1601
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1603     
1604     href: false,
1605     target: false,
1606     preventDefault: false,
1607     anchor : false,
1608     alt : false,
1609     fa: false,
1610
1611
1612     getAutoCreate : function()
1613     {
1614         var html = this.html || '';
1615         
1616         if (this.fa !== false) {
1617             html = '<i class="fa fa-' + this.fa + '"></i>';
1618         }
1619         var cfg = {
1620             tag: 'a'
1621         };
1622         // anchor's do not require html/href...
1623         if (this.anchor === false) {
1624             cfg.html = html;
1625             cfg.href = this.href || '#';
1626         } else {
1627             cfg.name = this.anchor;
1628             if (this.html !== false || this.fa !== false) {
1629                 cfg.html = html;
1630             }
1631             if (this.href !== false) {
1632                 cfg.href = this.href;
1633             }
1634         }
1635         
1636         if(this.alt !== false){
1637             cfg.alt = this.alt;
1638         }
1639         
1640         
1641         if(this.target !== false) {
1642             cfg.target = this.target;
1643         }
1644         
1645         return cfg;
1646     },
1647     
1648     initEvents: function() {
1649         
1650         if(!this.href || this.preventDefault){
1651             this.el.on('click', this.onClick, this);
1652         }
1653     },
1654     
1655     onClick : function(e)
1656     {
1657         if(this.preventDefault){
1658             e.preventDefault();
1659         }
1660         //Roo.log('img onclick');
1661         this.fireEvent('click', this, e);
1662     }
1663    
1664 });
1665
1666  /*
1667  * - LGPL
1668  *
1669  * header
1670  * 
1671  */
1672
1673 /**
1674  * @class Roo.bootstrap.Header
1675  * @extends Roo.bootstrap.Component
1676  * Bootstrap Header class
1677  * @cfg {String} html content of header
1678  * @cfg {Number} level (1|2|3|4|5|6) default 1
1679  * 
1680  * @constructor
1681  * Create a new Header
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.Header  = function(config){
1687     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1691     
1692     //href : false,
1693     html : false,
1694     level : 1,
1695     
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         
1701         
1702         var cfg = {
1703             tag: 'h' + (1 *this.level),
1704             html: this.html || ''
1705         } ;
1706         
1707         return cfg;
1708     }
1709    
1710 });
1711
1712  
1713
1714  /*
1715  * Based on:
1716  * Ext JS Library 1.1.1
1717  * Copyright(c) 2006-2007, Ext JS, LLC.
1718  *
1719  * Originally Released Under LGPL - original licence link has changed is not relivant.
1720  *
1721  * Fork - LGPL
1722  * <script type="text/javascript">
1723  */
1724  
1725 /**
1726  * @class Roo.bootstrap.MenuMgr
1727  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1728  * @singleton
1729  */
1730 Roo.bootstrap.MenuMgr = function(){
1731    var menus, active, groups = {}, attached = false, lastShow = new Date();
1732
1733    // private - called when first menu is created
1734    function init(){
1735        menus = {};
1736        active = new Roo.util.MixedCollection();
1737        Roo.get(document).addKeyListener(27, function(){
1738            if(active.length > 0){
1739                hideAll();
1740            }
1741        });
1742    }
1743
1744    // private
1745    function hideAll(){
1746        if(active && active.length > 0){
1747            var c = active.clone();
1748            c.each(function(m){
1749                m.hide();
1750            });
1751        }
1752    }
1753
1754    // private
1755    function onHide(m){
1756        active.remove(m);
1757        if(active.length < 1){
1758            Roo.get(document).un("mouseup", onMouseDown);
1759             
1760            attached = false;
1761        }
1762    }
1763
1764    // private
1765    function onShow(m){
1766        var last = active.last();
1767        lastShow = new Date();
1768        active.add(m);
1769        if(!attached){
1770           Roo.get(document).on("mouseup", onMouseDown);
1771            
1772            attached = true;
1773        }
1774        if(m.parentMenu){
1775           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776           m.parentMenu.activeChild = m;
1777        }else if(last && last.isVisible()){
1778           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1779        }
1780    }
1781
1782    // private
1783    function onBeforeHide(m){
1784        if(m.activeChild){
1785            m.activeChild.hide();
1786        }
1787        if(m.autoHideTimer){
1788            clearTimeout(m.autoHideTimer);
1789            delete m.autoHideTimer;
1790        }
1791    }
1792
1793    // private
1794    function onBeforeShow(m){
1795        var pm = m.parentMenu;
1796        if(!pm && !m.allowOtherMenus){
1797            hideAll();
1798        }else if(pm && pm.activeChild && active != m){
1799            pm.activeChild.hide();
1800        }
1801    }
1802
1803    // private this should really trigger on mouseup..
1804    function onMouseDown(e){
1805         Roo.log("on Mouse Up");
1806         
1807         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808             Roo.log("MenuManager hideAll");
1809             hideAll();
1810             e.stopEvent();
1811         }
1812         
1813         
1814    }
1815
1816    // private
1817    function onBeforeCheck(mi, state){
1818        if(state){
1819            var g = groups[mi.group];
1820            for(var i = 0, l = g.length; i < l; i++){
1821                if(g[i] != mi){
1822                    g[i].setChecked(false);
1823                }
1824            }
1825        }
1826    }
1827
1828    return {
1829
1830        /**
1831         * Hides all menus that are currently visible
1832         */
1833        hideAll : function(){
1834             hideAll();  
1835        },
1836
1837        // private
1838        register : function(menu){
1839            if(!menus){
1840                init();
1841            }
1842            menus[menu.id] = menu;
1843            menu.on("beforehide", onBeforeHide);
1844            menu.on("hide", onHide);
1845            menu.on("beforeshow", onBeforeShow);
1846            menu.on("show", onShow);
1847            var g = menu.group;
1848            if(g && menu.events["checkchange"]){
1849                if(!groups[g]){
1850                    groups[g] = [];
1851                }
1852                groups[g].push(menu);
1853                menu.on("checkchange", onCheck);
1854            }
1855        },
1856
1857         /**
1858          * Returns a {@link Roo.menu.Menu} object
1859          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860          * be used to generate and return a new Menu instance.
1861          */
1862        get : function(menu){
1863            if(typeof menu == "string"){ // menu id
1864                return menus[menu];
1865            }else if(menu.events){  // menu instance
1866                return menu;
1867            }
1868            /*else if(typeof menu.length == 'number'){ // array of menu items?
1869                return new Roo.bootstrap.Menu({items:menu});
1870            }else{ // otherwise, must be a config
1871                return new Roo.bootstrap.Menu(menu);
1872            }
1873            */
1874            return false;
1875        },
1876
1877        // private
1878        unregister : function(menu){
1879            delete menus[menu.id];
1880            menu.un("beforehide", onBeforeHide);
1881            menu.un("hide", onHide);
1882            menu.un("beforeshow", onBeforeShow);
1883            menu.un("show", onShow);
1884            var g = menu.group;
1885            if(g && menu.events["checkchange"]){
1886                groups[g].remove(menu);
1887                menu.un("checkchange", onCheck);
1888            }
1889        },
1890
1891        // private
1892        registerCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                if(!groups[g]){
1896                    groups[g] = [];
1897                }
1898                groups[g].push(menuItem);
1899                menuItem.on("beforecheckchange", onBeforeCheck);
1900            }
1901        },
1902
1903        // private
1904        unregisterCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                groups[g].remove(menuItem);
1908                menuItem.un("beforecheckchange", onBeforeCheck);
1909            }
1910        }
1911    };
1912 }();/*
1913  * - LGPL
1914  *
1915  * menu
1916  * 
1917  */
1918
1919 /**
1920  * @class Roo.bootstrap.Menu
1921  * @extends Roo.bootstrap.Component
1922  * Bootstrap Menu class - container for MenuItems
1923  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1925  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1926  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1927  * 
1928  * @constructor
1929  * Create a new Menu
1930  * @param {Object} config The config object
1931  */
1932
1933
1934 Roo.bootstrap.Menu = function(config){
1935     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936     if (this.registerMenu && this.type != 'treeview')  {
1937         Roo.bootstrap.MenuMgr.register(this);
1938     }
1939     this.addEvents({
1940         /**
1941          * @event beforeshow
1942          * Fires before this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         beforeshow : true,
1946         /**
1947          * @event beforehide
1948          * Fires before this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforehide : true,
1952         /**
1953          * @event show
1954          * Fires after this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         show : true,
1958         /**
1959          * @event hide
1960          * Fires after this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         hide : true,
1964         /**
1965          * @event click
1966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967          * @param {Roo.menu.Menu} this
1968          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969          * @param {Roo.EventObject} e
1970          */
1971         click : true,
1972         /**
1973          * @event mouseover
1974          * Fires when the mouse is hovering over this menu
1975          * @param {Roo.menu.Menu} this
1976          * @param {Roo.EventObject} e
1977          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1978          */
1979         mouseover : true,
1980         /**
1981          * @event mouseout
1982          * Fires when the mouse exits this menu
1983          * @param {Roo.menu.Menu} this
1984          * @param {Roo.EventObject} e
1985          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1986          */
1987         mouseout : true,
1988         /**
1989          * @event itemclick
1990          * Fires when a menu item contained in this menu is clicked
1991          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992          * @param {Roo.EventObject} e
1993          */
1994         itemclick: true
1995     });
1996     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1997 };
1998
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2000     
2001    /// html : false,
2002     //align : '',
2003     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2004     type: false,
2005     /**
2006      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2007      */
2008     registerMenu : true,
2009     
2010     menuItems :false, // stores the menu items..
2011     
2012     hidden:true,
2013         
2014     parentMenu : false,
2015     
2016     stopEvent : true,
2017     
2018     isLink : false,
2019     
2020     getChildContainer : function() {
2021         return this.el;  
2022     },
2023     
2024     getAutoCreate : function(){
2025          
2026         //if (['right'].indexOf(this.align)!==-1) {
2027         //    cfg.cn[1].cls += ' pull-right'
2028         //}
2029         
2030         
2031         var cfg = {
2032             tag : 'ul',
2033             cls : 'dropdown-menu' ,
2034             style : 'z-index:1000'
2035             
2036         };
2037         
2038         if (this.type === 'submenu') {
2039             cfg.cls = 'submenu active';
2040         }
2041         if (this.type === 'treeview') {
2042             cfg.cls = 'treeview-menu';
2043         }
2044         
2045         return cfg;
2046     },
2047     initEvents : function() {
2048         
2049        // Roo.log("ADD event");
2050        // Roo.log(this.triggerEl.dom);
2051         
2052         this.triggerEl.on('click', this.onTriggerClick, this);
2053         
2054         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2055         
2056         this.triggerEl.addClass('dropdown-toggle');
2057         
2058         if (Roo.isTouch) {
2059             this.el.on('touchstart'  , this.onTouch, this);
2060         }
2061         this.el.on('click' , this.onClick, this);
2062
2063         this.el.on("mouseover", this.onMouseOver, this);
2064         this.el.on("mouseout", this.onMouseOut, this);
2065         
2066     },
2067     
2068     findTargetItem : function(e)
2069     {
2070         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2071         if(!t){
2072             return false;
2073         }
2074         //Roo.log(t);         Roo.log(t.id);
2075         if(t && t.id){
2076             //Roo.log(this.menuitems);
2077             return this.menuitems.get(t.id);
2078             
2079             //return this.items.get(t.menuItemId);
2080         }
2081         
2082         return false;
2083     },
2084     
2085     onTouch : function(e) 
2086     {
2087         Roo.log("menu.onTouch");
2088         //e.stopEvent(); this make the user popdown broken
2089         this.onClick(e);
2090     },
2091     
2092     onClick : function(e)
2093     {
2094         Roo.log("menu.onClick");
2095         
2096         var t = this.findTargetItem(e);
2097         if(!t || t.isContainer){
2098             return;
2099         }
2100         Roo.log(e);
2101         /*
2102         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2103             if(t == this.activeItem && t.shouldDeactivate(e)){
2104                 this.activeItem.deactivate();
2105                 delete this.activeItem;
2106                 return;
2107             }
2108             if(t.canActivate){
2109                 this.setActiveItem(t, true);
2110             }
2111             return;
2112             
2113             
2114         }
2115         */
2116        
2117         Roo.log('pass click event');
2118         
2119         t.onClick(e);
2120         
2121         this.fireEvent("click", this, t, e);
2122         
2123         var _this = this;
2124         
2125         if(!t.href.length || t.href == '#'){
2126             (function() { _this.hide(); }).defer(100);
2127         }
2128         
2129     },
2130     
2131     onMouseOver : function(e){
2132         var t  = this.findTargetItem(e);
2133         //Roo.log(t);
2134         //if(t){
2135         //    if(t.canActivate && !t.disabled){
2136         //        this.setActiveItem(t, true);
2137         //    }
2138         //}
2139         
2140         this.fireEvent("mouseover", this, e, t);
2141     },
2142     isVisible : function(){
2143         return !this.hidden;
2144     },
2145      onMouseOut : function(e){
2146         var t  = this.findTargetItem(e);
2147         
2148         //if(t ){
2149         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2150         //        this.activeItem.deactivate();
2151         //        delete this.activeItem;
2152         //    }
2153         //}
2154         this.fireEvent("mouseout", this, e, t);
2155     },
2156     
2157     
2158     /**
2159      * Displays this menu relative to another element
2160      * @param {String/HTMLElement/Roo.Element} element The element to align to
2161      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162      * the element (defaults to this.defaultAlign)
2163      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2164      */
2165     show : function(el, pos, parentMenu){
2166         this.parentMenu = parentMenu;
2167         if(!this.el){
2168             this.render();
2169         }
2170         this.fireEvent("beforeshow", this);
2171         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2172     },
2173      /**
2174      * Displays this menu at a specific xy position
2175      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2177      */
2178     showAt : function(xy, parentMenu, /* private: */_e){
2179         this.parentMenu = parentMenu;
2180         if(!this.el){
2181             this.render();
2182         }
2183         if(_e !== false){
2184             this.fireEvent("beforeshow", this);
2185             //xy = this.el.adjustForConstraints(xy);
2186         }
2187         
2188         //this.el.show();
2189         this.hideMenuItems();
2190         this.hidden = false;
2191         this.triggerEl.addClass('open');
2192         
2193         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2195         }
2196         
2197         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2198             this.el.setXY(xy);
2199         }
2200         
2201         this.focus();
2202         this.fireEvent("show", this);
2203     },
2204     
2205     focus : function(){
2206         return;
2207         if(!this.hidden){
2208             this.doFocus.defer(50, this);
2209         }
2210     },
2211
2212     doFocus : function(){
2213         if(!this.hidden){
2214             this.focusEl.focus();
2215         }
2216     },
2217
2218     /**
2219      * Hides this menu and optionally all parent menus
2220      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2221      */
2222     hide : function(deep)
2223     {
2224         
2225         this.hideMenuItems();
2226         if(this.el && this.isVisible()){
2227             this.fireEvent("beforehide", this);
2228             if(this.activeItem){
2229                 this.activeItem.deactivate();
2230                 this.activeItem = null;
2231             }
2232             this.triggerEl.removeClass('open');;
2233             this.hidden = true;
2234             this.fireEvent("hide", this);
2235         }
2236         if(deep === true && this.parentMenu){
2237             this.parentMenu.hide(true);
2238         }
2239     },
2240     
2241     onTriggerClick : function(e)
2242     {
2243         Roo.log('trigger click');
2244         
2245         var target = e.getTarget();
2246         
2247         Roo.log(target.nodeName.toLowerCase());
2248         
2249         if(target.nodeName.toLowerCase() === 'i'){
2250             e.preventDefault();
2251         }
2252         
2253     },
2254     
2255     onTriggerPress  : function(e)
2256     {
2257         Roo.log('trigger press');
2258         //Roo.log(e.getTarget());
2259        // Roo.log(this.triggerEl.dom);
2260        
2261         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262         var pel = Roo.get(e.getTarget());
2263         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264             Roo.log('is treeview or dropdown?');
2265             return;
2266         }
2267         
2268         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2269             return;
2270         }
2271         
2272         if (this.isVisible()) {
2273             Roo.log('hide');
2274             this.hide();
2275         } else {
2276             Roo.log('show');
2277             this.show(this.triggerEl, false, false);
2278         }
2279         
2280         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2281             e.stopEvent();
2282         }
2283         
2284     },
2285        
2286     
2287     hideMenuItems : function()
2288     {
2289         Roo.log("hide Menu Items");
2290         if (!this.el) { 
2291             return;
2292         }
2293         //$(backdrop).remove()
2294         this.el.select('.open',true).each(function(aa) {
2295             
2296             aa.removeClass('open');
2297           //var parent = getParent($(this))
2298           //var relatedTarget = { relatedTarget: this }
2299           
2300            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301           //if (e.isDefaultPrevented()) return
2302            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2303         });
2304     },
2305     addxtypeChild : function (tree, cntr) {
2306         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2307           
2308         this.menuitems.add(comp);
2309         return comp;
2310
2311     },
2312     getEl : function()
2313     {
2314         Roo.log(this.el);
2315         return this.el;
2316     }
2317 });
2318
2319  
2320  /*
2321  * - LGPL
2322  *
2323  * menu item
2324  * 
2325  */
2326
2327
2328 /**
2329  * @class Roo.bootstrap.MenuItem
2330  * @extends Roo.bootstrap.Component
2331  * Bootstrap MenuItem class
2332  * @cfg {String} html the menu label
2333  * @cfg {String} href the link
2334  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2337  * @cfg {String} fa favicon to show on left of menu item.
2338  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2339  * 
2340  * 
2341  * @constructor
2342  * Create a new MenuItem
2343  * @param {Object} config The config object
2344  */
2345
2346
2347 Roo.bootstrap.MenuItem = function(config){
2348     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2349     this.addEvents({
2350         // raw events
2351         /**
2352          * @event click
2353          * The raw click event for the entire grid.
2354          * @param {Roo.bootstrap.MenuItem} this
2355          * @param {Roo.EventObject} e
2356          */
2357         "click" : true
2358     });
2359 };
2360
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2362     
2363     href : false,
2364     html : false,
2365     preventDefault: false,
2366     isContainer : false,
2367     active : false,
2368     fa: false,
2369     
2370     getAutoCreate : function(){
2371         
2372         if(this.isContainer){
2373             return {
2374                 tag: 'li',
2375                 cls: 'dropdown-menu-item'
2376             };
2377         }
2378         var ctag = {
2379             tag: 'span',
2380             html: 'Link'
2381         };
2382         
2383         var anc = {
2384             tag : 'a',
2385             href : '#',
2386             cn : [  ]
2387         };
2388         
2389         if (this.fa !== false) {
2390             anc.cn.push({
2391                 tag : 'i',
2392                 cls : 'fa fa-' + this.fa
2393             });
2394         }
2395         
2396         anc.cn.push(ctag);
2397         
2398         
2399         var cfg= {
2400             tag: 'li',
2401             cls: 'dropdown-menu-item',
2402             cn: [ anc ]
2403         };
2404         if (this.parent().type == 'treeview') {
2405             cfg.cls = 'treeview-menu';
2406         }
2407         if (this.active) {
2408             cfg.cls += ' active';
2409         }
2410         
2411         
2412         
2413         anc.href = this.href || cfg.cn[0].href ;
2414         ctag.html = this.html || cfg.cn[0].html ;
2415         return cfg;
2416     },
2417     
2418     initEvents: function()
2419     {
2420         if (this.parent().type == 'treeview') {
2421             this.el.select('a').on('click', this.onClick, this);
2422         }
2423         
2424         if (this.menu) {
2425             this.menu.parentType = this.xtype;
2426             this.menu.triggerEl = this.el;
2427             this.menu = this.addxtype(Roo.apply({}, this.menu));
2428         }
2429         
2430     },
2431     onClick : function(e)
2432     {
2433         Roo.log('item on click ');
2434         
2435         if(this.preventDefault){
2436             e.preventDefault();
2437         }
2438         //this.parent().hideMenuItems();
2439         
2440         this.fireEvent('click', this, e);
2441     },
2442     getEl : function()
2443     {
2444         return this.el;
2445     } 
2446 });
2447
2448  
2449
2450  /*
2451  * - LGPL
2452  *
2453  * menu separator
2454  * 
2455  */
2456
2457
2458 /**
2459  * @class Roo.bootstrap.MenuSeparator
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap MenuSeparator class
2462  * 
2463  * @constructor
2464  * Create a new MenuItem
2465  * @param {Object} config The config object
2466  */
2467
2468
2469 Roo.bootstrap.MenuSeparator = function(config){
2470     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2474     
2475     getAutoCreate : function(){
2476         var cfg = {
2477             cls: 'divider',
2478             tag : 'li'
2479         };
2480         
2481         return cfg;
2482     }
2483    
2484 });
2485
2486  
2487
2488  
2489 /*
2490 * Licence: LGPL
2491 */
2492
2493 /**
2494  * @class Roo.bootstrap.Modal
2495  * @extends Roo.bootstrap.Component
2496  * Bootstrap Modal class
2497  * @cfg {String} title Title of dialog
2498  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2500  * @cfg {Boolean} specificTitle default false
2501  * @cfg {Array} buttons Array of buttons or standard button set..
2502  * @cfg {String} buttonPosition (left|right|center) default right
2503  * @cfg {Boolean} animate default true
2504  * @cfg {Boolean} allow_close default true
2505  * @cfg {Boolean} fitwindow default false
2506  * @cfg {String} size (sm|lg) default empty
2507  *
2508  *
2509  * @constructor
2510  * Create a new Modal Dialog
2511  * @param {Object} config The config object
2512  */
2513
2514 Roo.bootstrap.Modal = function(config){
2515     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2516     this.addEvents({
2517         // raw events
2518         /**
2519          * @event btnclick
2520          * The raw btnclick event for the button
2521          * @param {Roo.EventObject} e
2522          */
2523         "btnclick" : true,
2524         /**
2525          * @event resize
2526          * Fire when dialog resize
2527          * @param {Roo.bootstrap.Modal} this
2528          * @param {Roo.EventObject} e
2529          */
2530         "resize" : true
2531     });
2532     this.buttons = this.buttons || [];
2533
2534     if (this.tmpl) {
2535         this.tmpl = Roo.factory(this.tmpl);
2536     }
2537
2538 };
2539
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2541
2542     title : 'test dialog',
2543
2544     buttons : false,
2545
2546     // set on load...
2547
2548     html: false,
2549
2550     tmp: false,
2551
2552     specificTitle: false,
2553
2554     buttonPosition: 'right',
2555
2556     allow_close : true,
2557
2558     animate : true,
2559
2560     fitwindow: false,
2561
2562
2563      // private
2564     dialogEl: false,
2565     bodyEl:  false,
2566     footerEl:  false,
2567     titleEl:  false,
2568     closeEl:  false,
2569
2570     size: '',
2571
2572
2573     onRender : function(ct, position)
2574     {
2575         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2576
2577         if(!this.el){
2578             var cfg = Roo.apply({},  this.getAutoCreate());
2579             cfg.id = Roo.id();
2580             //if(!cfg.name){
2581             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2582             //}
2583             //if (!cfg.name.length) {
2584             //    delete cfg.name;
2585            // }
2586             if (this.cls) {
2587                 cfg.cls += ' ' + this.cls;
2588             }
2589             if (this.style) {
2590                 cfg.style = this.style;
2591             }
2592             this.el = Roo.get(document.body).createChild(cfg, position);
2593         }
2594         //var type = this.el.dom.type;
2595
2596
2597         if(this.tabIndex !== undefined){
2598             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2599         }
2600
2601         this.dialogEl = this.el.select('.modal-dialog',true).first();
2602         this.bodyEl = this.el.select('.modal-body',true).first();
2603         this.closeEl = this.el.select('.modal-header .close', true).first();
2604         this.headerEl = this.el.select('.modal-header',true).first();
2605         this.titleEl = this.el.select('.modal-title',true).first();
2606         this.footerEl = this.el.select('.modal-footer',true).first();
2607
2608         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609         this.maskEl.enableDisplayMode("block");
2610         this.maskEl.hide();
2611         //this.el.addClass("x-dlg-modal");
2612
2613         if (this.buttons.length) {
2614             Roo.each(this.buttons, function(bb) {
2615                 var b = Roo.apply({}, bb);
2616                 b.xns = b.xns || Roo.bootstrap;
2617                 b.xtype = b.xtype || 'Button';
2618                 if (typeof(b.listeners) == 'undefined') {
2619                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2620                 }
2621
2622                 var btn = Roo.factory(b);
2623
2624                 btn.render(this.el.select('.modal-footer div').first());
2625
2626             },this);
2627         }
2628         // render the children.
2629         var nitems = [];
2630
2631         if(typeof(this.items) != 'undefined'){
2632             var items = this.items;
2633             delete this.items;
2634
2635             for(var i =0;i < items.length;i++) {
2636                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2637             }
2638         }
2639
2640         this.items = nitems;
2641
2642         // where are these used - they used to be body/close/footer
2643
2644
2645         this.initEvents();
2646         //this.el.addClass([this.fieldClass, this.cls]);
2647
2648     },
2649
2650     getAutoCreate : function(){
2651
2652
2653         var bdy = {
2654                 cls : 'modal-body',
2655                 html : this.html || ''
2656         };
2657
2658         var title = {
2659             tag: 'h4',
2660             cls : 'modal-title',
2661             html : this.title
2662         };
2663
2664         if(this.specificTitle){
2665             title = this.title;
2666
2667         };
2668
2669         var header = [];
2670         if (this.allow_close) {
2671             header.push({
2672                 tag: 'button',
2673                 cls : 'close',
2674                 html : '&times'
2675             });
2676         }
2677
2678         header.push(title);
2679
2680         var size = '';
2681
2682         if(this.size.length){
2683             size = 'modal-' + this.size;
2684         }
2685
2686         var modal = {
2687             cls: "modal",
2688             style : 'display: none',
2689             cn : [
2690                 {
2691                     cls: "modal-dialog " + size,
2692                     cn : [
2693                         {
2694                             cls : "modal-content",
2695                             cn : [
2696                                 {
2697                                     cls : 'modal-header',
2698                                     cn : header
2699                                 },
2700                                 bdy,
2701                                 {
2702                                     cls : 'modal-footer',
2703                                     cn : [
2704                                         {
2705                                             tag: 'div',
2706                                             cls: 'btn-' + this.buttonPosition
2707                                         }
2708                                     ]
2709
2710                                 }
2711
2712
2713                             ]
2714
2715                         }
2716                     ]
2717
2718                 }
2719             ]
2720         };
2721
2722         if(this.animate){
2723             modal.cls += ' fade';
2724         }
2725
2726         return modal;
2727
2728     },
2729     getChildContainer : function() {
2730
2731          return this.bodyEl;
2732
2733     },
2734     getButtonContainer : function() {
2735          return this.el.select('.modal-footer div',true).first();
2736
2737     },
2738     initEvents : function()
2739     {
2740         if (this.allow_close) {
2741             this.closeEl.on('click', this.hide, this);
2742         }
2743         Roo.EventManager.onWindowResize(this.resize, this, true);
2744
2745
2746     },
2747
2748     resize : function()
2749     {
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2751         if (this.fitwindow) {
2752             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2754             this.setSize(w,h);
2755         }
2756     },
2757
2758     setSize : function(w,h)
2759     {
2760         if (!w && !h) {
2761             return;
2762         }
2763         this.resizeTo(w,h);
2764     },
2765
2766     show : function() {
2767
2768         if (!this.rendered) {
2769             this.render();
2770         }
2771
2772         this.el.setStyle('display', 'block');
2773
2774         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2775             var _this = this;
2776             (function(){
2777                 this.el.addClass('in');
2778             }).defer(50, this);
2779         }else{
2780             this.el.addClass('in');
2781
2782         }
2783
2784         // not sure how we can show data in here..
2785         //if (this.tmpl) {
2786         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2787         //}
2788
2789         Roo.get(document.body).addClass("x-body-masked");
2790         
2791         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2792         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2793         this.maskEl.show();
2794         
2795         this.resize();
2796         
2797         this.fireEvent('show', this);
2798
2799         // set zindex here - otherwise it appears to be ignored...
2800         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2801
2802         (function () {
2803             this.items.forEach( function(e) {
2804                 e.layout ? e.layout() : false;
2805
2806             });
2807         }).defer(100,this);
2808
2809     },
2810     hide : function()
2811     {
2812         if(this.fireEvent("beforehide", this) !== false){
2813             this.maskEl.hide();
2814             Roo.get(document.body).removeClass("x-body-masked");
2815             this.el.removeClass('in');
2816             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2817
2818             if(this.animate){ // why
2819                 var _this = this;
2820                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2821             }else{
2822                 this.el.setStyle('display', 'none');
2823             }
2824             this.fireEvent('hide', this);
2825         }
2826     },
2827
2828     addButton : function(str, cb)
2829     {
2830
2831
2832         var b = Roo.apply({}, { html : str } );
2833         b.xns = b.xns || Roo.bootstrap;
2834         b.xtype = b.xtype || 'Button';
2835         if (typeof(b.listeners) == 'undefined') {
2836             b.listeners = { click : cb.createDelegate(this)  };
2837         }
2838
2839         var btn = Roo.factory(b);
2840
2841         btn.render(this.el.select('.modal-footer div').first());
2842
2843         return btn;
2844
2845     },
2846
2847     setDefaultButton : function(btn)
2848     {
2849         //this.el.select('.modal-footer').()
2850     },
2851     diff : false,
2852
2853     resizeTo: function(w,h)
2854     {
2855         // skip.. ?? why??
2856
2857         this.dialogEl.setWidth(w);
2858         if (this.diff === false) {
2859             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2860         }
2861
2862         this.bodyEl.setHeight(h-this.diff);
2863
2864         this.fireEvent('resize', this);
2865
2866     },
2867     setContentSize  : function(w, h)
2868     {
2869
2870     },
2871     onButtonClick: function(btn,e)
2872     {
2873         //Roo.log([a,b,c]);
2874         this.fireEvent('btnclick', btn.name, e);
2875     },
2876      /**
2877      * Set the title of the Dialog
2878      * @param {String} str new Title
2879      */
2880     setTitle: function(str) {
2881         this.titleEl.dom.innerHTML = str;
2882     },
2883     /**
2884      * Set the body of the Dialog
2885      * @param {String} str new Title
2886      */
2887     setBody: function(str) {
2888         this.bodyEl.dom.innerHTML = str;
2889     },
2890     /**
2891      * Set the body of the Dialog using the template
2892      * @param {Obj} data - apply this data to the template and replace the body contents.
2893      */
2894     applyBody: function(obj)
2895     {
2896         if (!this.tmpl) {
2897             Roo.log("Error - using apply Body without a template");
2898             //code
2899         }
2900         this.tmpl.overwrite(this.bodyEl, obj);
2901     }
2902
2903 });
2904
2905
2906 Roo.apply(Roo.bootstrap.Modal,  {
2907     /**
2908          * Button config that displays a single OK button
2909          * @type Object
2910          */
2911         OK :  [{
2912             name : 'ok',
2913             weight : 'primary',
2914             html : 'OK'
2915         }],
2916         /**
2917          * Button config that displays Yes and No buttons
2918          * @type Object
2919          */
2920         YESNO : [
2921             {
2922                 name  : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name  :'yes',
2927                 weight : 'primary',
2928                 html : 'Yes'
2929             }
2930         ],
2931
2932         /**
2933          * Button config that displays OK and Cancel buttons
2934          * @type Object
2935          */
2936         OKCANCEL : [
2937             {
2938                name : 'cancel',
2939                 html : 'Cancel'
2940             },
2941             {
2942                 name : 'ok',
2943                 weight : 'primary',
2944                 html : 'OK'
2945             }
2946         ],
2947         /**
2948          * Button config that displays Yes, No and Cancel buttons
2949          * @type Object
2950          */
2951         YESNOCANCEL : [
2952             {
2953                 name : 'yes',
2954                 weight : 'primary',
2955                 html : 'Yes'
2956             },
2957             {
2958                 name : 'no',
2959                 html : 'No'
2960             },
2961             {
2962                 name : 'cancel',
2963                 html : 'Cancel'
2964             }
2965         ],
2966         
2967         zIndex : 10001
2968 });
2969 /*
2970  * - LGPL
2971  *
2972  * messagebox - can be used as a replace
2973  * 
2974  */
2975 /**
2976  * @class Roo.MessageBox
2977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2978  * Example usage:
2979  *<pre><code>
2980 // Basic alert:
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2982
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2985     if (btn == 'ok'){
2986         // process text value...
2987     }
2988 });
2989
2990 // Show a dialog using config options:
2991 Roo.Msg.show({
2992    title:'Save Changes?',
2993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994    buttons: Roo.Msg.YESNOCANCEL,
2995    fn: processResult,
2996    animEl: 'elId'
2997 });
2998 </code></pre>
2999  * @singleton
3000  */
3001 Roo.bootstrap.MessageBox = function(){
3002     var dlg, opt, mask, waitTimer;
3003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004     var buttons, activeTextEl, bwidth;
3005
3006     
3007     // private
3008     var handleButton = function(button){
3009         dlg.hide();
3010         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3011     };
3012
3013     // private
3014     var handleHide = function(){
3015         if(opt && opt.cls){
3016             dlg.el.removeClass(opt.cls);
3017         }
3018         //if(waitTimer){
3019         //    Roo.TaskMgr.stop(waitTimer);
3020         //    waitTimer = null;
3021         //}
3022     };
3023
3024     // private
3025     var updateButtons = function(b){
3026         var width = 0;
3027         if(!b){
3028             buttons["ok"].hide();
3029             buttons["cancel"].hide();
3030             buttons["yes"].hide();
3031             buttons["no"].hide();
3032             //dlg.footer.dom.style.display = 'none';
3033             return width;
3034         }
3035         dlg.footerEl.dom.style.display = '';
3036         for(var k in buttons){
3037             if(typeof buttons[k] != "function"){
3038                 if(b[k]){
3039                     buttons[k].show();
3040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041                     width += buttons[k].el.getWidth()+15;
3042                 }else{
3043                     buttons[k].hide();
3044                 }
3045             }
3046         }
3047         return width;
3048     };
3049
3050     // private
3051     var handleEsc = function(d, k, e){
3052         if(opt && opt.closable !== false){
3053             dlg.hide();
3054         }
3055         if(e){
3056             e.stopEvent();
3057         }
3058     };
3059
3060     return {
3061         /**
3062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063          * @return {Roo.BasicDialog} The BasicDialog element
3064          */
3065         getDialog : function(){
3066            if(!dlg){
3067                 dlg = new Roo.bootstrap.Modal( {
3068                     //draggable: true,
3069                     //resizable:false,
3070                     //constraintoviewport:false,
3071                     //fixedcenter:true,
3072                     //collapsible : false,
3073                     //shim:true,
3074                     //modal: true,
3075                 //    width: 'auto',
3076                   //  height:100,
3077                     //buttonAlign:"center",
3078                     closeClick : function(){
3079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3080                             handleButton("no");
3081                         }else{
3082                             handleButton("cancel");
3083                         }
3084                     }
3085                 });
3086                 dlg.render();
3087                 dlg.on("hide", handleHide);
3088                 mask = dlg.mask;
3089                 //dlg.addKeyListener(27, handleEsc);
3090                 buttons = {};
3091                 this.buttons = buttons;
3092                 var bt = this.buttonText;
3093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3097                 //Roo.log(buttons);
3098                 bodyEl = dlg.bodyEl.createChild({
3099
3100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101                         '<textarea class="roo-mb-textarea"></textarea>' +
3102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3103                 });
3104                 msgEl = bodyEl.dom.firstChild;
3105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106                 textboxEl.enableDisplayMode();
3107                 textboxEl.addKeyListener([10,13], function(){
3108                     if(dlg.isVisible() && opt && opt.buttons){
3109                         if(opt.buttons.ok){
3110                             handleButton("ok");
3111                         }else if(opt.buttons.yes){
3112                             handleButton("yes");
3113                         }
3114                     }
3115                 });
3116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117                 textareaEl.enableDisplayMode();
3118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119                 progressEl.enableDisplayMode();
3120                 
3121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122                 //var pf = progressEl.dom.firstChild;
3123                 //if (pf) {
3124                     //pp = Roo.get(pf.firstChild);
3125                     //pp.setHeight(pf.offsetHeight);
3126                 //}
3127                 
3128             }
3129             return dlg;
3130         },
3131
3132         /**
3133          * Updates the message box body text
3134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135          * the XHTML-compliant non-breaking space character '&amp;#160;')
3136          * @return {Roo.MessageBox} This message box
3137          */
3138         updateText : function(text)
3139         {
3140             if(!dlg.isVisible() && !opt.width){
3141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3143             }
3144             msgEl.innerHTML = text || '&#160;';
3145       
3146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3148             var w = Math.max(
3149                     Math.min(opt.width || cw , this.maxWidth), 
3150                     Math.max(opt.minWidth || this.minWidth, bwidth)
3151             );
3152             if(opt.prompt){
3153                 activeTextEl.setWidth(w);
3154             }
3155             if(dlg.isVisible()){
3156                 dlg.fixedcenter = false;
3157             }
3158             // to big, make it scroll. = But as usual stupid IE does not support
3159             // !important..
3160             
3161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3164             } else {
3165                 bodyEl.dom.style.height = '';
3166                 bodyEl.dom.style.overflowY = '';
3167             }
3168             if (cw > w) {
3169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.overflowX = '';
3172             }
3173             
3174             dlg.setContentSize(w, bodyEl.getHeight());
3175             if(dlg.isVisible()){
3176                 dlg.fixedcenter = true;
3177             }
3178             return this;
3179         },
3180
3181         /**
3182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateProgress : function(value, text){
3189             if(text){
3190                 this.updateText(text);
3191             }
3192             if (pp) { // weird bug on my firefox - for some reason this is not defined
3193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3194             }
3195             return this;
3196         },        
3197
3198         /**
3199          * Returns true if the message box is currently displayed
3200          * @return {Boolean} True if the message box is visible, else false
3201          */
3202         isVisible : function(){
3203             return dlg && dlg.isVisible();  
3204         },
3205
3206         /**
3207          * Hides the message box if it is displayed
3208          */
3209         hide : function(){
3210             if(this.isVisible()){
3211                 dlg.hide();
3212             }  
3213         },
3214
3215         /**
3216          * Displays a new message box, or reinitializes an existing message box, based on the config options
3217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218          * The following config object properties are supported:
3219          * <pre>
3220 Property    Type             Description
3221 ----------  ---------------  ------------------------------------------------------------------------------------
3222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3223                                    closes (defaults to undefined)
3224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3227                                    progress and wait dialogs will ignore this property and always hide the
3228                                    close button as they can only be closed programmatically.
3229 cls               String           A custom CSS class to apply to the message box element
3230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3231                                    displayed (defaults to 75)
3232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3233                                    function will be btn (the name of the button that was clicked, if applicable,
3234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3235                                    Progress and wait dialogs will ignore this option since they do not respond to
3236                                    user actions and can only be closed programmatically, so any required function
3237                                    should be called by the same code after it closes the dialog.
3238 icon              String           A CSS class that provides a background image to be used as an icon for
3239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3242 modal             Boolean          False to allow user interaction with the page while the message box is
3243                                    displayed (defaults to true)
3244 msg               String           A string that will replace the existing message box body text (defaults
3245                                    to the XHTML-compliant non-breaking space character '&#160;')
3246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3247 progress          Boolean          True to display a progress bar (defaults to false)
3248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3251 title             String           The title text
3252 value             String           The string value to set into the active textbox element if displayed
3253 wait              Boolean          True to display a progress bar (defaults to false)
3254 width             Number           The width of the dialog in pixels
3255 </pre>
3256          *
3257          * Example usage:
3258          * <pre><code>
3259 Roo.Msg.show({
3260    title: 'Address',
3261    msg: 'Please enter your address:',
3262    width: 300,
3263    buttons: Roo.MessageBox.OKCANCEL,
3264    multiline: true,
3265    fn: saveAddress,
3266    animEl: 'addAddressBtn'
3267 });
3268 </code></pre>
3269          * @param {Object} config Configuration options
3270          * @return {Roo.MessageBox} This message box
3271          */
3272         show : function(options)
3273         {
3274             
3275             // this causes nightmares if you show one dialog after another
3276             // especially on callbacks..
3277              
3278             if(this.isVisible()){
3279                 
3280                 this.hide();
3281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3283                 Roo.log("New Dialog Message:" +  options.msg )
3284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3286                 
3287             }
3288             var d = this.getDialog();
3289             opt = options;
3290             d.setTitle(opt.title || "&#160;");
3291             d.closeEl.setDisplayed(opt.closable !== false);
3292             activeTextEl = textboxEl;
3293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3294             if(opt.prompt){
3295                 if(opt.multiline){
3296                     textboxEl.hide();
3297                     textareaEl.show();
3298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3299                         opt.multiline : this.defaultTextHeight);
3300                     activeTextEl = textareaEl;
3301                 }else{
3302                     textboxEl.show();
3303                     textareaEl.hide();
3304                 }
3305             }else{
3306                 textboxEl.hide();
3307                 textareaEl.hide();
3308             }
3309             progressEl.setDisplayed(opt.progress === true);
3310             this.updateProgress(0);
3311             activeTextEl.dom.value = opt.value || "";
3312             if(opt.prompt){
3313                 dlg.setDefaultButton(activeTextEl);
3314             }else{
3315                 var bs = opt.buttons;
3316                 var db = null;
3317                 if(bs && bs.ok){
3318                     db = buttons["ok"];
3319                 }else if(bs && bs.yes){
3320                     db = buttons["yes"];
3321                 }
3322                 dlg.setDefaultButton(db);
3323             }
3324             bwidth = updateButtons(opt.buttons);
3325             this.updateText(opt.msg);
3326             if(opt.cls){
3327                 d.el.addClass(opt.cls);
3328             }
3329             d.proxyDrag = opt.proxyDrag === true;
3330             d.modal = opt.modal !== false;
3331             d.mask = opt.modal !== false ? mask : false;
3332             if(!d.isVisible()){
3333                 // force it to the end of the z-index stack so it gets a cursor in FF
3334                 document.body.appendChild(dlg.el.dom);
3335                 d.animateTarget = null;
3336                 d.show(options.animEl);
3337             }
3338             return this;
3339         },
3340
3341         /**
3342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344          * and closing the message box when the process is complete.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @return {Roo.MessageBox} This message box
3348          */
3349         progress : function(title, msg){
3350             this.show({
3351                 title : title,
3352                 msg : msg,
3353                 buttons: false,
3354                 progress:true,
3355                 closable:false,
3356                 minWidth: this.minProgressWidth,
3357                 modal : true
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364          * If a callback function is passed it will be called after the user clicks the button, and the
3365          * id of the button that was clicked will be passed as the only parameter to the callback
3366          * (could also be the top-right close button).
3367          * @param {String} title The title bar text
3368          * @param {String} msg The message box body text
3369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370          * @param {Object} scope (optional) The scope of the callback function
3371          * @return {Roo.MessageBox} This message box
3372          */
3373         alert : function(title, msg, fn, scope)
3374         {
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OK,
3379                 fn: fn,
3380                 closable : false,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390          * You are responsible for closing the message box when the process is complete.
3391          * @param {String} msg The message box body text
3392          * @param {String} title (optional) The title bar text
3393          * @return {Roo.MessageBox} This message box
3394          */
3395         wait : function(msg, title){
3396             this.show({
3397                 title : title,
3398                 msg : msg,
3399                 buttons: false,
3400                 closable:false,
3401                 progress:true,
3402                 modal:true,
3403                 width:300,
3404                 wait:true
3405             });
3406             waitTimer = Roo.TaskMgr.start({
3407                 run: function(i){
3408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3409                 },
3410                 interval: 1000
3411             });
3412             return this;
3413         },
3414
3415         /**
3416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         confirm : function(title, msg, fn, scope){
3426             this.show({
3427                 title : title,
3428                 msg : msg,
3429                 buttons: this.YESNO,
3430                 fn: fn,
3431                 scope : scope,
3432                 modal : true
3433             });
3434             return this;
3435         },
3436
3437         /**
3438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441          * (could also be the top-right close button) and the text that was entered will be passed as the two
3442          * parameters to the callback.
3443          * @param {String} title The title bar text
3444          * @param {String} msg The message box body text
3445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446          * @param {Object} scope (optional) The scope of the callback function
3447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         prompt : function(title, msg, fn, scope, multiline){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: this.OKCANCEL,
3456                 fn: fn,
3457                 minWidth:250,
3458                 scope : scope,
3459                 prompt:true,
3460                 multiline: multiline,
3461                 modal : true
3462             });
3463             return this;
3464         },
3465
3466         /**
3467          * Button config that displays a single OK button
3468          * @type Object
3469          */
3470         OK : {ok:true},
3471         /**
3472          * Button config that displays Yes and No buttons
3473          * @type Object
3474          */
3475         YESNO : {yes:true, no:true},
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : {ok:true, cancel:true},
3481         /**
3482          * Button config that displays Yes, No and Cancel buttons
3483          * @type Object
3484          */
3485         YESNOCANCEL : {yes:true, no:true, cancel:true},
3486
3487         /**
3488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3489          * @type Number
3490          */
3491         defaultTextHeight : 75,
3492         /**
3493          * The maximum width in pixels of the message box (defaults to 600)
3494          * @type Number
3495          */
3496         maxWidth : 600,
3497         /**
3498          * The minimum width in pixels of the message box (defaults to 100)
3499          * @type Number
3500          */
3501         minWidth : 100,
3502         /**
3503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3505          * @type Number
3506          */
3507         minProgressWidth : 250,
3508         /**
3509          * An object containing the default button text strings that can be overriden for localized language support.
3510          * Supported properties are: ok, cancel, yes and no.
3511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3512          * @type Object
3513          */
3514         buttonText : {
3515             ok : "OK",
3516             cancel : "Cancel",
3517             yes : "Yes",
3518             no : "No"
3519         }
3520     };
3521 }();
3522
3523 /**
3524  * Shorthand for {@link Roo.MessageBox}
3525  */
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3528 /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.Navbar
3537  * @extends Roo.bootstrap.Component
3538  * Bootstrap Navbar class
3539
3540  * @constructor
3541  * Create a new Navbar
3542  * @param {Object} config The config object
3543  */
3544
3545
3546 Roo.bootstrap.Navbar = function(config){
3547     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3548     this.addEvents({
3549         // raw events
3550         /**
3551          * @event beforetoggle
3552          * Fire before toggle the menu
3553          * @param {Roo.EventObject} e
3554          */
3555         "beforetoggle" : true
3556     });
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3560     
3561     
3562    
3563     // private
3564     navItems : false,
3565     loadMask : false,
3566     
3567     
3568     getAutoCreate : function(){
3569         
3570         
3571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3572         
3573     },
3574     
3575     initEvents :function ()
3576     {
3577         //Roo.log(this.el.select('.navbar-toggle',true));
3578         this.el.select('.navbar-toggle',true).on('click', function() {
3579             if(this.fireEvent('beforetoggle', this) !== false){
3580                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3581             }
3582             
3583         }, this);
3584         
3585         var mark = {
3586             tag: "div",
3587             cls:"x-dlg-mask"
3588         };
3589         
3590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3591         
3592         var size = this.el.getSize();
3593         this.maskEl.setSize(size.width, size.height);
3594         this.maskEl.enableDisplayMode("block");
3595         this.maskEl.hide();
3596         
3597         if(this.loadMask){
3598             this.maskEl.show();
3599         }
3600     },
3601     
3602     
3603     getChildContainer : function()
3604     {
3605         if (this.el.select('.collapse').getCount()) {
3606             return this.el.select('.collapse',true).first();
3607         }
3608         
3609         return this.el;
3610     },
3611     
3612     mask : function()
3613     {
3614         this.maskEl.show();
3615     },
3616     
3617     unmask : function()
3618     {
3619         this.maskEl.hide();
3620     } 
3621     
3622     
3623     
3624     
3625 });
3626
3627
3628
3629  
3630
3631  /*
3632  * - LGPL
3633  *
3634  * navbar
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSimplebar
3640  * @extends Roo.bootstrap.Navbar
3641  * Bootstrap Sidebar class
3642  *
3643  * @cfg {Boolean} inverse is inverted color
3644  * 
3645  * @cfg {String} type (nav | pills | tabs)
3646  * @cfg {Boolean} arrangement stacked | justified
3647  * @cfg {String} align (left | right) alignment
3648  * 
3649  * @cfg {Boolean} main (true|false) main nav bar? default false
3650  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3651  * 
3652  * @cfg {String} tag (header|footer|nav|div) default is nav 
3653
3654  * 
3655  * 
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSimplebar = function(config){
3664     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3668     
3669     inverse: false,
3670     
3671     type: false,
3672     arrangement: '',
3673     align : false,
3674     
3675     
3676     
3677     main : false,
3678     
3679     
3680     tag : false,
3681     
3682     
3683     getAutoCreate : function(){
3684         
3685         
3686         var cfg = {
3687             tag : this.tag || 'div',
3688             cls : 'navbar'
3689         };
3690           
3691         
3692         cfg.cn = [
3693             {
3694                 cls: 'nav',
3695                 tag : 'ul'
3696             }
3697         ];
3698         
3699          
3700         this.type = this.type || 'nav';
3701         if (['tabs','pills'].indexOf(this.type)!==-1) {
3702             cfg.cn[0].cls += ' nav-' + this.type
3703         
3704         
3705         } else {
3706             if (this.type!=='nav') {
3707                 Roo.log('nav type must be nav/tabs/pills')
3708             }
3709             cfg.cn[0].cls += ' navbar-nav'
3710         }
3711         
3712         
3713         
3714         
3715         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.arrangement;
3717         }
3718         
3719         
3720         if (this.align === 'right') {
3721             cfg.cn[0].cls += ' navbar-right';
3722         }
3723         
3724         if (this.inverse) {
3725             cfg.cls += ' navbar-inverse';
3726             
3727         }
3728         
3729         
3730         return cfg;
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  
3744        /*
3745  * - LGPL
3746  *
3747  * navbar
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavHeaderbar
3753  * @extends Roo.bootstrap.NavSimplebar
3754  * Bootstrap Sidebar class
3755  *
3756  * @cfg {String} brand what is brand
3757  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758  * @cfg {String} brand_href href of the brand
3759  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3760  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3763  * 
3764  * @constructor
3765  * Create a new Sidebar
3766  * @param {Object} config The config object
3767  */
3768
3769
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3772       
3773 };
3774
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3776     
3777     position: '',
3778     brand: '',
3779     brand_href: false,
3780     srButton : true,
3781     autohide : false,
3782     desktopCenter : false,
3783    
3784     
3785     getAutoCreate : function(){
3786         
3787         var   cfg = {
3788             tag: this.nav || 'nav',
3789             cls: 'navbar',
3790             role: 'navigation',
3791             cn: []
3792         };
3793         
3794         var cn = cfg.cn;
3795         if (this.desktopCenter) {
3796             cn.push({cls : 'container', cn : []});
3797             cn = cn[0].cn;
3798         }
3799         
3800         if(this.srButton){
3801             cn.push({
3802                 tag: 'div',
3803                 cls: 'navbar-header',
3804                 cn: [
3805                     {
3806                         tag: 'button',
3807                         type: 'button',
3808                         cls: 'navbar-toggle',
3809                         'data-toggle': 'collapse',
3810                         cn: [
3811                             {
3812                                 tag: 'span',
3813                                 cls: 'sr-only',
3814                                 html: 'Toggle navigation'
3815                             },
3816                             {
3817                                 tag: 'span',
3818                                 cls: 'icon-bar'
3819                             },
3820                             {
3821                                 tag: 'span',
3822                                 cls: 'icon-bar'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             }
3828                         ]
3829                     }
3830                 ]
3831             });
3832         }
3833         
3834         cn.push({
3835             tag: 'div',
3836             cls: 'collapse navbar-collapse',
3837             cn : []
3838         });
3839         
3840         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3841         
3842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843             cfg.cls += ' navbar-' + this.position;
3844             
3845             // tag can override this..
3846             
3847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3848         }
3849         
3850         if (this.brand !== '') {
3851             cn[0].cn.push({
3852                 tag: 'a',
3853                 href: this.brand_href ? this.brand_href : '#',
3854                 cls: 'navbar-brand',
3855                 cn: [
3856                 this.brand
3857                 ]
3858             });
3859         }
3860         
3861         if(this.main){
3862             cfg.cls += ' main-nav';
3863         }
3864         
3865         
3866         return cfg;
3867
3868         
3869     },
3870     getHeaderChildContainer : function()
3871     {
3872         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873             return this.el.select('.navbar-header',true).first();
3874         }
3875         
3876         return this.getChildContainer();
3877     },
3878     
3879     
3880     initEvents : function()
3881     {
3882         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3883         
3884         if (this.autohide) {
3885             
3886             var prevScroll = 0;
3887             var ft = this.el;
3888             
3889             Roo.get(document).on('scroll',function(e) {
3890                 var ns = Roo.get(document).getScroll().top;
3891                 var os = prevScroll;
3892                 prevScroll = ns;
3893                 
3894                 if(ns > os){
3895                     ft.removeClass('slideDown');
3896                     ft.addClass('slideUp');
3897                     return;
3898                 }
3899                 ft.removeClass('slideUp');
3900                 ft.addClass('slideDown');
3901                  
3902               
3903           },this);
3904         }
3905     }    
3906     
3907 });
3908
3909
3910
3911  
3912
3913  /*
3914  * - LGPL
3915  *
3916  * navbar
3917  * 
3918  */
3919
3920 /**
3921  * @class Roo.bootstrap.NavSidebar
3922  * @extends Roo.bootstrap.Navbar
3923  * Bootstrap Sidebar class
3924  * 
3925  * @constructor
3926  * Create a new Sidebar
3927  * @param {Object} config The config object
3928  */
3929
3930
3931 Roo.bootstrap.NavSidebar = function(config){
3932     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3936     
3937     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3938     
3939     getAutoCreate : function(){
3940         
3941         
3942         return  {
3943             tag: 'div',
3944             cls: 'sidebar sidebar-nav'
3945         };
3946     
3947         
3948     }
3949     
3950     
3951     
3952 });
3953
3954
3955
3956  
3957
3958  /*
3959  * - LGPL
3960  *
3961  * nav group
3962  * 
3963  */
3964
3965 /**
3966  * @class Roo.bootstrap.NavGroup
3967  * @extends Roo.bootstrap.Component
3968  * Bootstrap NavGroup class
3969  * @cfg {String} align (left|right)
3970  * @cfg {Boolean} inverse
3971  * @cfg {String} type (nav|pills|tab) default nav
3972  * @cfg {String} navId - reference Id for navbar.
3973
3974  * 
3975  * @constructor
3976  * Create a new nav group
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.NavGroup = function(config){
3981     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3982     this.navItems = [];
3983    
3984     Roo.bootstrap.NavGroup.register(this);
3985      this.addEvents({
3986         /**
3987              * @event changed
3988              * Fires when the active item changes
3989              * @param {Roo.bootstrap.NavGroup} this
3990              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3992          */
3993         'changed': true
3994      });
3995     
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3999     
4000     align: '',
4001     inverse: false,
4002     form: false,
4003     type: 'nav',
4004     navId : '',
4005     // private
4006     
4007     navItems : false, 
4008     
4009     getAutoCreate : function()
4010     {
4011         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4012         
4013         cfg = {
4014             tag : 'ul',
4015             cls: 'nav' 
4016         };
4017         
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cls += ' nav-' + this.type
4020         } else {
4021             if (this.type!=='nav') {
4022                 Roo.log('nav type must be nav/tabs/pills')
4023             }
4024             cfg.cls += ' navbar-nav'
4025         }
4026         
4027         if (this.parent().sidebar) {
4028             cfg = {
4029                 tag: 'ul',
4030                 cls: 'dashboard-menu sidebar-menu'
4031             };
4032             
4033             return cfg;
4034         }
4035         
4036         if (this.form === true) {
4037             cfg = {
4038                 tag: 'form',
4039                 cls: 'navbar-form'
4040             };
4041             
4042             if (this.align === 'right') {
4043                 cfg.cls += ' navbar-right';
4044             } else {
4045                 cfg.cls += ' navbar-left';
4046             }
4047         }
4048         
4049         if (this.align === 'right') {
4050             cfg.cls += ' navbar-right';
4051         }
4052         
4053         if (this.inverse) {
4054             cfg.cls += ' navbar-inverse';
4055             
4056         }
4057         
4058         
4059         return cfg;
4060     },
4061     /**
4062     * sets the active Navigation item
4063     * @param {Roo.bootstrap.NavItem} the new current navitem
4064     */
4065     setActiveItem : function(item)
4066     {
4067         var prev = false;
4068         Roo.each(this.navItems, function(v){
4069             if (v == item) {
4070                 return ;
4071             }
4072             if (v.isActive()) {
4073                 v.setActive(false, true);
4074                 prev = v;
4075                 
4076             }
4077             
4078         });
4079
4080         item.setActive(true, true);
4081         this.fireEvent('changed', this, item, prev);
4082         
4083         
4084     },
4085     /**
4086     * gets the active Navigation item
4087     * @return {Roo.bootstrap.NavItem} the current navitem
4088     */
4089     getActive : function()
4090     {
4091         
4092         var prev = false;
4093         Roo.each(this.navItems, function(v){
4094             
4095             if (v.isActive()) {
4096                 prev = v;
4097                 
4098             }
4099             
4100         });
4101         return prev;
4102     },
4103     
4104     indexOfNav : function()
4105     {
4106         
4107         var prev = false;
4108         Roo.each(this.navItems, function(v,i){
4109             
4110             if (v.isActive()) {
4111                 prev = i;
4112                 
4113             }
4114             
4115         });
4116         return prev;
4117     },
4118     /**
4119     * adds a Navigation item
4120     * @param {Roo.bootstrap.NavItem} the navitem to add
4121     */
4122     addItem : function(cfg)
4123     {
4124         var cn = new Roo.bootstrap.NavItem(cfg);
4125         this.register(cn);
4126         cn.parentId = this.id;
4127         cn.onRender(this.el, null);
4128         return cn;
4129     },
4130     /**
4131     * register a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     register : function(item)
4135     {
4136         this.navItems.push( item);
4137         item.navId = this.navId;
4138     
4139     },
4140     
4141     /**
4142     * clear all the Navigation item
4143     */
4144    
4145     clearAll : function()
4146     {
4147         this.navItems = [];
4148         this.el.dom.innerHTML = '';
4149     },
4150     
4151     getNavItem: function(tabId)
4152     {
4153         var ret = false;
4154         Roo.each(this.navItems, function(e) {
4155             if (e.tabId == tabId) {
4156                ret =  e;
4157                return false;
4158             }
4159             return true;
4160             
4161         });
4162         return ret;
4163     },
4164     
4165     setActiveNext : function()
4166     {
4167         var i = this.indexOfNav(this.getActive());
4168         if (i > this.navItems.length) {
4169             return;
4170         }
4171         this.setActiveItem(this.navItems[i+1]);
4172     },
4173     setActivePrev : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i  < 1) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i-1]);
4180     },
4181     clearWasActive : function(except) {
4182         Roo.each(this.navItems, function(e) {
4183             if (e.tabId != except.tabId && e.was_active) {
4184                e.was_active = false;
4185                return false;
4186             }
4187             return true;
4188             
4189         });
4190     },
4191     getWasActive : function ()
4192     {
4193         var r = false;
4194         Roo.each(this.navItems, function(e) {
4195             if (e.was_active) {
4196                r = e;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202         return r;
4203     }
4204     
4205     
4206 });
4207
4208  
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4210     
4211     groups: {},
4212      /**
4213     * register a Navigation Group
4214     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4215     */
4216     register : function(navgrp)
4217     {
4218         this.groups[navgrp.navId] = navgrp;
4219         
4220     },
4221     /**
4222     * fetch a Navigation Group based on the navigation ID
4223     * @param {string} the navgroup to add
4224     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4225     */
4226     get: function(navId) {
4227         if (typeof(this.groups[navId]) == 'undefined') {
4228             return false;
4229             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4230         }
4231         return this.groups[navId] ;
4232     }
4233     
4234     
4235     
4236 });
4237
4238  /*
4239  * - LGPL
4240  *
4241  * row
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavItem
4247  * @extends Roo.bootstrap.Component
4248  * Bootstrap Navbar.NavItem class
4249  * @cfg {String} href  link to
4250  * @cfg {String} html content of button
4251  * @cfg {String} badge text inside badge
4252  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253  * @cfg {String} glyphicon name of glyphicon
4254  * @cfg {String} icon name of font awesome icon
4255  * @cfg {Boolean} active Is item active
4256  * @cfg {Boolean} disabled Is item disabled
4257  
4258  * @cfg {Boolean} preventDefault (true | false) default false
4259  * @cfg {String} tabId the tab that this item activates.
4260  * @cfg {String} tagtype (a|span) render as a href or span?
4261  * @cfg {Boolean} animateRef (true|false) link to element default false  
4262   
4263  * @constructor
4264  * Create a new Navbar Item
4265  * @param {Object} config The config object
4266  */
4267 Roo.bootstrap.NavItem = function(config){
4268     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4269     this.addEvents({
4270         // raw events
4271         /**
4272          * @event click
4273          * The raw click event for the entire grid.
4274          * @param {Roo.EventObject} e
4275          */
4276         "click" : true,
4277          /**
4278             * @event changed
4279             * Fires when the active item active state changes
4280             * @param {Roo.bootstrap.NavItem} this
4281             * @param {boolean} state the new state
4282              
4283          */
4284         'changed': true,
4285         /**
4286             * @event scrollto
4287             * Fires when scroll to element
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {Object} options
4290             * @param {Roo.EventObject} e
4291              
4292          */
4293         'scrollto': true
4294     });
4295    
4296 };
4297
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4299     
4300     href: false,
4301     html: '',
4302     badge: '',
4303     icon: false,
4304     glyphicon: false,
4305     active: false,
4306     preventDefault : false,
4307     tabId : false,
4308     tagtype : 'a',
4309     disabled : false,
4310     animateRef : false,
4311     was_active : false,
4312     
4313     getAutoCreate : function(){
4314          
4315         var cfg = {
4316             tag: 'li',
4317             cls: 'nav-item'
4318             
4319         };
4320         
4321         if (this.active) {
4322             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4323         }
4324         if (this.disabled) {
4325             cfg.cls += ' disabled';
4326         }
4327         
4328         if (this.href || this.html || this.glyphicon || this.icon) {
4329             cfg.cn = [
4330                 {
4331                     tag: this.tagtype,
4332                     href : this.href || "#",
4333                     html: this.html || ''
4334                 }
4335             ];
4336             
4337             if (this.icon) {
4338                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4339             }
4340
4341             if(this.glyphicon) {
4342                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4343             }
4344             
4345             if (this.menu) {
4346                 
4347                 cfg.cn[0].html += " <span class='caret'></span>";
4348              
4349             }
4350             
4351             if (this.badge !== '') {
4352                  
4353                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4354             }
4355         }
4356         
4357         
4358         
4359         return cfg;
4360     },
4361     initEvents: function() 
4362     {
4363         if (typeof (this.menu) != 'undefined') {
4364             this.menu.parentType = this.xtype;
4365             this.menu.triggerEl = this.el;
4366             this.menu = this.addxtype(Roo.apply({}, this.menu));
4367         }
4368         
4369         this.el.select('a',true).on('click', this.onClick, this);
4370         
4371         if(this.tagtype == 'span'){
4372             this.el.select('span',true).on('click', this.onClick, this);
4373         }
4374        
4375         // at this point parent should be available..
4376         this.parent().register(this);
4377     },
4378     
4379     onClick : function(e)
4380     {
4381         if (e.getTarget('.dropdown-menu-item')) {
4382             // did you click on a menu itemm.... - then don't trigger onclick..
4383             return;
4384         }
4385         
4386         if(
4387                 this.preventDefault || 
4388                 this.href == '#' 
4389         ){
4390             Roo.log("NavItem - prevent Default?");
4391             e.preventDefault();
4392         }
4393         
4394         if (this.disabled) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (tg && tg.transition) {
4400             Roo.log("waiting for the transitionend");
4401             return;
4402         }
4403         
4404         
4405         
4406         //Roo.log("fire event clicked");
4407         if(this.fireEvent('click', this, e) === false){
4408             return;
4409         };
4410         
4411         if(this.tagtype == 'span'){
4412             return;
4413         }
4414         
4415         //Roo.log(this.href);
4416         var ael = this.el.select('a',true).first();
4417         //Roo.log(ael);
4418         
4419         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422                 return; // ignore... - it's a 'hash' to another page.
4423             }
4424             Roo.log("NavItem - prevent Default?");
4425             e.preventDefault();
4426             this.scrollToElement(e);
4427         }
4428         
4429         
4430         var p =  this.parent();
4431    
4432         if (['tabs','pills'].indexOf(p.type)!==-1) {
4433             if (typeof(p.setActiveItem) !== 'undefined') {
4434                 p.setActiveItem(this);
4435             }
4436         }
4437         
4438         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440             // remove the collapsed menu expand...
4441             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4442         }
4443     },
4444     
4445     isActive: function () {
4446         return this.active
4447     },
4448     setActive : function(state, fire, is_was_active)
4449     {
4450         if (this.active && !state && this.navId) {
4451             this.was_active = true;
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 nv.clearWasActive(this);
4455             }
4456             
4457         }
4458         this.active = state;
4459         
4460         if (!state ) {
4461             this.el.removeClass('active');
4462         } else if (!this.el.hasClass('active')) {
4463             this.el.addClass('active');
4464         }
4465         if (fire) {
4466             this.fireEvent('changed', this, state);
4467         }
4468         
4469         // show a panel if it's registered and related..
4470         
4471         if (!this.navId || !this.tabId || !state || is_was_active) {
4472             return;
4473         }
4474         
4475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4476         if (!tg) {
4477             return;
4478         }
4479         var pan = tg.getPanelByName(this.tabId);
4480         if (!pan) {
4481             return;
4482         }
4483         // if we can not flip to new panel - go back to old nav highlight..
4484         if (false == tg.showPanel(pan)) {
4485             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4486             if (nv) {
4487                 var onav = nv.getWasActive();
4488                 if (onav) {
4489                     onav.setActive(true, false, true);
4490                 }
4491             }
4492             
4493         }
4494         
4495         
4496         
4497     },
4498      // this should not be here...
4499     setDisabled : function(state)
4500     {
4501         this.disabled = state;
4502         if (!state ) {
4503             this.el.removeClass('disabled');
4504         } else if (!this.el.hasClass('disabled')) {
4505             this.el.addClass('disabled');
4506         }
4507         
4508     },
4509     
4510     /**
4511      * Fetch the element to display the tooltip on.
4512      * @return {Roo.Element} defaults to this.el
4513      */
4514     tooltipEl : function()
4515     {
4516         return this.el.select('' + this.tagtype + '', true).first();
4517     },
4518     
4519     scrollToElement : function(e)
4520     {
4521         var c = document.body;
4522         
4523         /*
4524          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4525          */
4526         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527             c = document.documentElement;
4528         }
4529         
4530         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4531         
4532         if(!target){
4533             return;
4534         }
4535
4536         var o = target.calcOffsetsTo(c);
4537         
4538         var options = {
4539             target : target,
4540             value : o[1]
4541         };
4542         
4543         this.fireEvent('scrollto', this, options, e);
4544         
4545         Roo.get(c).scrollTo('top', options.value, true);
4546         
4547         return;
4548     }
4549 });
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * sidebar item
4556  *
4557  *  li
4558  *    <span> icon </span>
4559  *    <span> text </span>
4560  *    <span>badge </span>
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavSidebarItem
4565  * @extends Roo.bootstrap.NavItem
4566  * Bootstrap Navbar.NavSidebarItem class
4567  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568  * {bool} open is the menu open
4569  * @constructor
4570  * Create a new Navbar Button
4571  * @param {Object} config The config object
4572  */
4573 Roo.bootstrap.NavSidebarItem = function(config){
4574     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4575     this.addEvents({
4576         // raw events
4577         /**
4578          * @event click
4579          * The raw click event for the entire grid.
4580          * @param {Roo.EventObject} e
4581          */
4582         "click" : true,
4583          /**
4584             * @event changed
4585             * Fires when the active item active state changes
4586             * @param {Roo.bootstrap.NavSidebarItem} this
4587             * @param {boolean} state the new state
4588              
4589          */
4590         'changed': true
4591     });
4592    
4593 };
4594
4595 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4596     
4597     badgeWeight : 'default',
4598     
4599     open: false,
4600     
4601     getAutoCreate : function(){
4602         
4603         
4604         var a = {
4605                 tag: 'a',
4606                 href : this.href || '#',
4607                 cls: '',
4608                 html : '',
4609                 cn : []
4610         };
4611         var cfg = {
4612             tag: 'li',
4613             cls: '',
4614             cn: [ a ]
4615         };
4616         var span = {
4617             tag: 'span',
4618             html : this.html || ''
4619         };
4620         
4621         
4622         if (this.active) {
4623             cfg.cls += ' active';
4624         }
4625         
4626         if (this.disabled) {
4627             cfg.cls += ' disabled';
4628         }
4629         if (this.open) {
4630             cfg.cls += ' open x-open';
4631         }
4632         // left icon..
4633         if (this.glyphicon || this.icon) {
4634             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4635             a.cn.push({ tag : 'i', cls : c }) ;
4636         }
4637         // html..
4638         a.cn.push(span);
4639         // then badge..
4640         if (this.badge !== '') {
4641             
4642             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4643         }
4644         // fi
4645         if (this.menu) {
4646             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4647             a.cls += 'dropdown-toggle treeview' ;
4648         }
4649         
4650         return cfg;
4651          
4652            
4653     },
4654     
4655     initEvents : function()
4656     { 
4657         if (typeof (this.menu) != 'undefined') {
4658             this.menu.parentType = this.xtype;
4659             this.menu.triggerEl = this.el;
4660             this.menu = this.addxtype(Roo.apply({}, this.menu));
4661         }
4662         
4663         this.el.on('click', this.onClick, this);
4664        
4665     
4666         if(this.badge !== ''){
4667  
4668             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4669         }
4670         
4671     },
4672     
4673     onClick : function(e)
4674     {
4675         if(this.disabled){
4676             e.preventDefault();
4677             return;
4678         }
4679         
4680         if(this.preventDefault){
4681             e.preventDefault();
4682         }
4683         
4684         this.fireEvent('click', this);
4685     },
4686     
4687     disable : function()
4688     {
4689         this.setDisabled(true);
4690     },
4691     
4692     enable : function()
4693     {
4694         this.setDisabled(false);
4695     },
4696     
4697     setDisabled : function(state)
4698     {
4699         if(this.disabled == state){
4700             return;
4701         }
4702         
4703         this.disabled = state;
4704         
4705         if (state) {
4706             this.el.addClass('disabled');
4707             return;
4708         }
4709         
4710         this.el.removeClass('disabled');
4711         
4712         return;
4713     },
4714     
4715     setActive : function(state)
4716     {
4717         if(this.active == state){
4718             return;
4719         }
4720         
4721         this.active = state;
4722         
4723         if (state) {
4724             this.el.addClass('active');
4725             return;
4726         }
4727         
4728         this.el.removeClass('active');
4729         
4730         return;
4731     },
4732     
4733     isActive: function () 
4734     {
4735         return this.active;
4736     },
4737     
4738     setBadge : function(str)
4739     {
4740         if(!this.badgeEl){
4741             return;
4742         }
4743         
4744         this.badgeEl.dom.innerHTML = str;
4745     }
4746     
4747    
4748      
4749  
4750 });
4751  
4752
4753  /*
4754  * - LGPL
4755  *
4756  * row
4757  * 
4758  */
4759
4760 /**
4761  * @class Roo.bootstrap.Row
4762  * @extends Roo.bootstrap.Component
4763  * Bootstrap Row class (contains columns...)
4764  * 
4765  * @constructor
4766  * Create a new Row
4767  * @param {Object} config The config object
4768  */
4769
4770 Roo.bootstrap.Row = function(config){
4771     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4772 };
4773
4774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4775     
4776     getAutoCreate : function(){
4777        return {
4778             cls: 'row clearfix'
4779        };
4780     }
4781     
4782     
4783 });
4784
4785  
4786
4787  /*
4788  * - LGPL
4789  *
4790  * element
4791  * 
4792  */
4793
4794 /**
4795  * @class Roo.bootstrap.Element
4796  * @extends Roo.bootstrap.Component
4797  * Bootstrap Element class
4798  * @cfg {String} html contents of the element
4799  * @cfg {String} tag tag of the element
4800  * @cfg {String} cls class of the element
4801  * @cfg {Boolean} preventDefault (true|false) default false
4802  * @cfg {Boolean} clickable (true|false) default false
4803  * 
4804  * @constructor
4805  * Create a new Element
4806  * @param {Object} config The config object
4807  */
4808
4809 Roo.bootstrap.Element = function(config){
4810     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4811     
4812     this.addEvents({
4813         // raw events
4814         /**
4815          * @event click
4816          * When a element is chick
4817          * @param {Roo.bootstrap.Element} this
4818          * @param {Roo.EventObject} e
4819          */
4820         "click" : true
4821     });
4822 };
4823
4824 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4825     
4826     tag: 'div',
4827     cls: '',
4828     html: '',
4829     preventDefault: false, 
4830     clickable: false,
4831     
4832     getAutoCreate : function(){
4833         
4834         var cfg = {
4835             tag: this.tag,
4836             cls: this.cls,
4837             html: this.html
4838         };
4839         
4840         return cfg;
4841     },
4842     
4843     initEvents: function() 
4844     {
4845         Roo.bootstrap.Element.superclass.initEvents.call(this);
4846         
4847         if(this.clickable){
4848             this.el.on('click', this.onClick, this);
4849         }
4850         
4851     },
4852     
4853     onClick : function(e)
4854     {
4855         if(this.preventDefault){
4856             e.preventDefault();
4857         }
4858         
4859         this.fireEvent('click', this, e);
4860     },
4861     
4862     getValue : function()
4863     {
4864         return this.el.dom.innerHTML;
4865     },
4866     
4867     setValue : function(value)
4868     {
4869         this.el.dom.innerHTML = value;
4870     }
4871    
4872 });
4873
4874  
4875
4876  /*
4877  * - LGPL
4878  *
4879  * pagination
4880  * 
4881  */
4882
4883 /**
4884  * @class Roo.bootstrap.Pagination
4885  * @extends Roo.bootstrap.Component
4886  * Bootstrap Pagination class
4887  * @cfg {String} size xs | sm | md | lg
4888  * @cfg {Boolean} inverse false | true
4889  * 
4890  * @constructor
4891  * Create a new Pagination
4892  * @param {Object} config The config object
4893  */
4894
4895 Roo.bootstrap.Pagination = function(config){
4896     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4897 };
4898
4899 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4900     
4901     cls: false,
4902     size: false,
4903     inverse: false,
4904     
4905     getAutoCreate : function(){
4906         var cfg = {
4907             tag: 'ul',
4908                 cls: 'pagination'
4909         };
4910         if (this.inverse) {
4911             cfg.cls += ' inverse';
4912         }
4913         if (this.html) {
4914             cfg.html=this.html;
4915         }
4916         if (this.cls) {
4917             cfg.cls += " " + this.cls;
4918         }
4919         return cfg;
4920     }
4921    
4922 });
4923
4924  
4925
4926  /*
4927  * - LGPL
4928  *
4929  * Pagination item
4930  * 
4931  */
4932
4933
4934 /**
4935  * @class Roo.bootstrap.PaginationItem
4936  * @extends Roo.bootstrap.Component
4937  * Bootstrap PaginationItem class
4938  * @cfg {String} html text
4939  * @cfg {String} href the link
4940  * @cfg {Boolean} preventDefault (true | false) default true
4941  * @cfg {Boolean} active (true | false) default false
4942  * @cfg {Boolean} disabled default false
4943  * 
4944  * 
4945  * @constructor
4946  * Create a new PaginationItem
4947  * @param {Object} config The config object
4948  */
4949
4950
4951 Roo.bootstrap.PaginationItem = function(config){
4952     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4953     this.addEvents({
4954         // raw events
4955         /**
4956          * @event click
4957          * The raw click event for the entire grid.
4958          * @param {Roo.EventObject} e
4959          */
4960         "click" : true
4961     });
4962 };
4963
4964 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4965     
4966     href : false,
4967     html : false,
4968     preventDefault: true,
4969     active : false,
4970     cls : false,
4971     disabled: false,
4972     
4973     getAutoCreate : function(){
4974         var cfg= {
4975             tag: 'li',
4976             cn: [
4977                 {
4978                     tag : 'a',
4979                     href : this.href ? this.href : '#',
4980                     html : this.html ? this.html : ''
4981                 }
4982             ]
4983         };
4984         
4985         if(this.cls){
4986             cfg.cls = this.cls;
4987         }
4988         
4989         if(this.disabled){
4990             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4991         }
4992         
4993         if(this.active){
4994             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4995         }
4996         
4997         return cfg;
4998     },
4999     
5000     initEvents: function() {
5001         
5002         this.el.on('click', this.onClick, this);
5003         
5004     },
5005     onClick : function(e)
5006     {
5007         Roo.log('PaginationItem on click ');
5008         if(this.preventDefault){
5009             e.preventDefault();
5010         }
5011         
5012         if(this.disabled){
5013             return;
5014         }
5015         
5016         this.fireEvent('click', this, e);
5017     }
5018    
5019 });
5020
5021  
5022
5023  /*
5024  * - LGPL
5025  *
5026  * slider
5027  * 
5028  */
5029
5030
5031 /**
5032  * @class Roo.bootstrap.Slider
5033  * @extends Roo.bootstrap.Component
5034  * Bootstrap Slider class
5035  *    
5036  * @constructor
5037  * Create a new Slider
5038  * @param {Object} config The config object
5039  */
5040
5041 Roo.bootstrap.Slider = function(config){
5042     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5043 };
5044
5045 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5046     
5047     getAutoCreate : function(){
5048         
5049         var cfg = {
5050             tag: 'div',
5051             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5052             cn: [
5053                 {
5054                     tag: 'a',
5055                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5056                 }
5057             ]
5058         };
5059         
5060         return cfg;
5061     }
5062    
5063 });
5064
5065  /*
5066  * Based on:
5067  * Ext JS Library 1.1.1
5068  * Copyright(c) 2006-2007, Ext JS, LLC.
5069  *
5070  * Originally Released Under LGPL - original licence link has changed is not relivant.
5071  *
5072  * Fork - LGPL
5073  * <script type="text/javascript">
5074  */
5075  
5076
5077 /**
5078  * @class Roo.grid.ColumnModel
5079  * @extends Roo.util.Observable
5080  * This is the default implementation of a ColumnModel used by the Grid. It defines
5081  * the columns in the grid.
5082  * <br>Usage:<br>
5083  <pre><code>
5084  var colModel = new Roo.grid.ColumnModel([
5085         {header: "Ticker", width: 60, sortable: true, locked: true},
5086         {header: "Company Name", width: 150, sortable: true},
5087         {header: "Market Cap.", width: 100, sortable: true},
5088         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5089         {header: "Employees", width: 100, sortable: true, resizable: false}
5090  ]);
5091  </code></pre>
5092  * <p>
5093  
5094  * The config options listed for this class are options which may appear in each
5095  * individual column definition.
5096  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5097  * @constructor
5098  * @param {Object} config An Array of column config objects. See this class's
5099  * config objects for details.
5100 */
5101 Roo.grid.ColumnModel = function(config){
5102         /**
5103      * The config passed into the constructor
5104      */
5105     this.config = config;
5106     this.lookup = {};
5107
5108     // if no id, create one
5109     // if the column does not have a dataIndex mapping,
5110     // map it to the order it is in the config
5111     for(var i = 0, len = config.length; i < len; i++){
5112         var c = config[i];
5113         if(typeof c.dataIndex == "undefined"){
5114             c.dataIndex = i;
5115         }
5116         if(typeof c.renderer == "string"){
5117             c.renderer = Roo.util.Format[c.renderer];
5118         }
5119         if(typeof c.id == "undefined"){
5120             c.id = Roo.id();
5121         }
5122         if(c.editor && c.editor.xtype){
5123             c.editor  = Roo.factory(c.editor, Roo.grid);
5124         }
5125         if(c.editor && c.editor.isFormField){
5126             c.editor = new Roo.grid.GridEditor(c.editor);
5127         }
5128         this.lookup[c.id] = c;
5129     }
5130
5131     /**
5132      * The width of columns which have no width specified (defaults to 100)
5133      * @type Number
5134      */
5135     this.defaultWidth = 100;
5136
5137     /**
5138      * Default sortable of columns which have no sortable specified (defaults to false)
5139      * @type Boolean
5140      */
5141     this.defaultSortable = false;
5142
5143     this.addEvents({
5144         /**
5145              * @event widthchange
5146              * Fires when the width of a column changes.
5147              * @param {ColumnModel} this
5148              * @param {Number} columnIndex The column index
5149              * @param {Number} newWidth The new width
5150              */
5151             "widthchange": true,
5152         /**
5153              * @event headerchange
5154              * Fires when the text of a header changes.
5155              * @param {ColumnModel} this
5156              * @param {Number} columnIndex The column index
5157              * @param {Number} newText The new header text
5158              */
5159             "headerchange": true,
5160         /**
5161              * @event hiddenchange
5162              * Fires when a column is hidden or "unhidden".
5163              * @param {ColumnModel} this
5164              * @param {Number} columnIndex The column index
5165              * @param {Boolean} hidden true if hidden, false otherwise
5166              */
5167             "hiddenchange": true,
5168             /**
5169          * @event columnmoved
5170          * Fires when a column is moved.
5171          * @param {ColumnModel} this
5172          * @param {Number} oldIndex
5173          * @param {Number} newIndex
5174          */
5175         "columnmoved" : true,
5176         /**
5177          * @event columlockchange
5178          * Fires when a column's locked state is changed
5179          * @param {ColumnModel} this
5180          * @param {Number} colIndex
5181          * @param {Boolean} locked true if locked
5182          */
5183         "columnlockchange" : true
5184     });
5185     Roo.grid.ColumnModel.superclass.constructor.call(this);
5186 };
5187 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5188     /**
5189      * @cfg {String} header The header text to display in the Grid view.
5190      */
5191     /**
5192      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5193      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5194      * specified, the column's index is used as an index into the Record's data Array.
5195      */
5196     /**
5197      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5198      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5199      */
5200     /**
5201      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5202      * Defaults to the value of the {@link #defaultSortable} property.
5203      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5204      */
5205     /**
5206      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5207      */
5208     /**
5209      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5210      */
5211     /**
5212      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5213      */
5214     /**
5215      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5216      */
5217     /**
5218      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5219      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5220      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5221      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5222      */
5223        /**
5224      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5225      */
5226     /**
5227      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5228      */
5229     /**
5230      * @cfg {String} cursor (Optional)
5231      */
5232     /**
5233      * @cfg {String} tooltip (Optional)
5234      */
5235     /**
5236      * @cfg {Number} xs (Optional)
5237      */
5238     /**
5239      * @cfg {Number} sm (Optional)
5240      */
5241     /**
5242      * @cfg {Number} md (Optional)
5243      */
5244     /**
5245      * @cfg {Number} lg (Optional)
5246      */
5247     /**
5248      * Returns the id of the column at the specified index.
5249      * @param {Number} index The column index
5250      * @return {String} the id
5251      */
5252     getColumnId : function(index){
5253         return this.config[index].id;
5254     },
5255
5256     /**
5257      * Returns the column for a specified id.
5258      * @param {String} id The column id
5259      * @return {Object} the column
5260      */
5261     getColumnById : function(id){
5262         return this.lookup[id];
5263     },
5264
5265     
5266     /**
5267      * Returns the column for a specified dataIndex.
5268      * @param {String} dataIndex The column dataIndex
5269      * @return {Object|Boolean} the column or false if not found
5270      */
5271     getColumnByDataIndex: function(dataIndex){
5272         var index = this.findColumnIndex(dataIndex);
5273         return index > -1 ? this.config[index] : false;
5274     },
5275     
5276     /**
5277      * Returns the index for a specified column id.
5278      * @param {String} id The column id
5279      * @return {Number} the index, or -1 if not found
5280      */
5281     getIndexById : function(id){
5282         for(var i = 0, len = this.config.length; i < len; i++){
5283             if(this.config[i].id == id){
5284                 return i;
5285             }
5286         }
5287         return -1;
5288     },
5289     
5290     /**
5291      * Returns the index for a specified column dataIndex.
5292      * @param {String} dataIndex The column dataIndex
5293      * @return {Number} the index, or -1 if not found
5294      */
5295     
5296     findColumnIndex : function(dataIndex){
5297         for(var i = 0, len = this.config.length; i < len; i++){
5298             if(this.config[i].dataIndex == dataIndex){
5299                 return i;
5300             }
5301         }
5302         return -1;
5303     },
5304     
5305     
5306     moveColumn : function(oldIndex, newIndex){
5307         var c = this.config[oldIndex];
5308         this.config.splice(oldIndex, 1);
5309         this.config.splice(newIndex, 0, c);
5310         this.dataMap = null;
5311         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5312     },
5313
5314     isLocked : function(colIndex){
5315         return this.config[colIndex].locked === true;
5316     },
5317
5318     setLocked : function(colIndex, value, suppressEvent){
5319         if(this.isLocked(colIndex) == value){
5320             return;
5321         }
5322         this.config[colIndex].locked = value;
5323         if(!suppressEvent){
5324             this.fireEvent("columnlockchange", this, colIndex, value);
5325         }
5326     },
5327
5328     getTotalLockedWidth : function(){
5329         var totalWidth = 0;
5330         for(var i = 0; i < this.config.length; i++){
5331             if(this.isLocked(i) && !this.isHidden(i)){
5332                 this.totalWidth += this.getColumnWidth(i);
5333             }
5334         }
5335         return totalWidth;
5336     },
5337
5338     getLockedCount : function(){
5339         for(var i = 0, len = this.config.length; i < len; i++){
5340             if(!this.isLocked(i)){
5341                 return i;
5342             }
5343         }
5344         
5345         return this.config.length;
5346     },
5347
5348     /**
5349      * Returns the number of columns.
5350      * @return {Number}
5351      */
5352     getColumnCount : function(visibleOnly){
5353         if(visibleOnly === true){
5354             var c = 0;
5355             for(var i = 0, len = this.config.length; i < len; i++){
5356                 if(!this.isHidden(i)){
5357                     c++;
5358                 }
5359             }
5360             return c;
5361         }
5362         return this.config.length;
5363     },
5364
5365     /**
5366      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5367      * @param {Function} fn
5368      * @param {Object} scope (optional)
5369      * @return {Array} result
5370      */
5371     getColumnsBy : function(fn, scope){
5372         var r = [];
5373         for(var i = 0, len = this.config.length; i < len; i++){
5374             var c = this.config[i];
5375             if(fn.call(scope||this, c, i) === true){
5376                 r[r.length] = c;
5377             }
5378         }
5379         return r;
5380     },
5381
5382     /**
5383      * Returns true if the specified column is sortable.
5384      * @param {Number} col The column index
5385      * @return {Boolean}
5386      */
5387     isSortable : function(col){
5388         if(typeof this.config[col].sortable == "undefined"){
5389             return this.defaultSortable;
5390         }
5391         return this.config[col].sortable;
5392     },
5393
5394     /**
5395      * Returns the rendering (formatting) function defined for the column.
5396      * @param {Number} col The column index.
5397      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5398      */
5399     getRenderer : function(col){
5400         if(!this.config[col].renderer){
5401             return Roo.grid.ColumnModel.defaultRenderer;
5402         }
5403         return this.config[col].renderer;
5404     },
5405
5406     /**
5407      * Sets the rendering (formatting) function for a column.
5408      * @param {Number} col The column index
5409      * @param {Function} fn The function to use to process the cell's raw data
5410      * to return HTML markup for the grid view. The render function is called with
5411      * the following parameters:<ul>
5412      * <li>Data value.</li>
5413      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5414      * <li>css A CSS style string to apply to the table cell.</li>
5415      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5416      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5417      * <li>Row index</li>
5418      * <li>Column index</li>
5419      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5420      */
5421     setRenderer : function(col, fn){
5422         this.config[col].renderer = fn;
5423     },
5424
5425     /**
5426      * Returns the width for the specified column.
5427      * @param {Number} col The column index
5428      * @return {Number}
5429      */
5430     getColumnWidth : function(col){
5431         return this.config[col].width * 1 || this.defaultWidth;
5432     },
5433
5434     /**
5435      * Sets the width for a column.
5436      * @param {Number} col The column index
5437      * @param {Number} width The new width
5438      */
5439     setColumnWidth : function(col, width, suppressEvent){
5440         this.config[col].width = width;
5441         this.totalWidth = null;
5442         if(!suppressEvent){
5443              this.fireEvent("widthchange", this, col, width);
5444         }
5445     },
5446
5447     /**
5448      * Returns the total width of all columns.
5449      * @param {Boolean} includeHidden True to include hidden column widths
5450      * @return {Number}
5451      */
5452     getTotalWidth : function(includeHidden){
5453         if(!this.totalWidth){
5454             this.totalWidth = 0;
5455             for(var i = 0, len = this.config.length; i < len; i++){
5456                 if(includeHidden || !this.isHidden(i)){
5457                     this.totalWidth += this.getColumnWidth(i);
5458                 }
5459             }
5460         }
5461         return this.totalWidth;
5462     },
5463
5464     /**
5465      * Returns the header for the specified column.
5466      * @param {Number} col The column index
5467      * @return {String}
5468      */
5469     getColumnHeader : function(col){
5470         return this.config[col].header;
5471     },
5472
5473     /**
5474      * Sets the header for a column.
5475      * @param {Number} col The column index
5476      * @param {String} header The new header
5477      */
5478     setColumnHeader : function(col, header){
5479         this.config[col].header = header;
5480         this.fireEvent("headerchange", this, col, header);
5481     },
5482
5483     /**
5484      * Returns the tooltip for the specified column.
5485      * @param {Number} col The column index
5486      * @return {String}
5487      */
5488     getColumnTooltip : function(col){
5489             return this.config[col].tooltip;
5490     },
5491     /**
5492      * Sets the tooltip for a column.
5493      * @param {Number} col The column index
5494      * @param {String} tooltip The new tooltip
5495      */
5496     setColumnTooltip : function(col, tooltip){
5497             this.config[col].tooltip = tooltip;
5498     },
5499
5500     /**
5501      * Returns the dataIndex for the specified column.
5502      * @param {Number} col The column index
5503      * @return {Number}
5504      */
5505     getDataIndex : function(col){
5506         return this.config[col].dataIndex;
5507     },
5508
5509     /**
5510      * Sets the dataIndex for a column.
5511      * @param {Number} col The column index
5512      * @param {Number} dataIndex The new dataIndex
5513      */
5514     setDataIndex : function(col, dataIndex){
5515         this.config[col].dataIndex = dataIndex;
5516     },
5517
5518     
5519     
5520     /**
5521      * Returns true if the cell is editable.
5522      * @param {Number} colIndex The column index
5523      * @param {Number} rowIndex The row index - this is nto actually used..?
5524      * @return {Boolean}
5525      */
5526     isCellEditable : function(colIndex, rowIndex){
5527         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5528     },
5529
5530     /**
5531      * Returns the editor defined for the cell/column.
5532      * return false or null to disable editing.
5533      * @param {Number} colIndex The column index
5534      * @param {Number} rowIndex The row index
5535      * @return {Object}
5536      */
5537     getCellEditor : function(colIndex, rowIndex){
5538         return this.config[colIndex].editor;
5539     },
5540
5541     /**
5542      * Sets if a column is editable.
5543      * @param {Number} col The column index
5544      * @param {Boolean} editable True if the column is editable
5545      */
5546     setEditable : function(col, editable){
5547         this.config[col].editable = editable;
5548     },
5549
5550
5551     /**
5552      * Returns true if the column is hidden.
5553      * @param {Number} colIndex The column index
5554      * @return {Boolean}
5555      */
5556     isHidden : function(colIndex){
5557         return this.config[colIndex].hidden;
5558     },
5559
5560
5561     /**
5562      * Returns true if the column width cannot be changed
5563      */
5564     isFixed : function(colIndex){
5565         return this.config[colIndex].fixed;
5566     },
5567
5568     /**
5569      * Returns true if the column can be resized
5570      * @return {Boolean}
5571      */
5572     isResizable : function(colIndex){
5573         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5574     },
5575     /**
5576      * Sets if a column is hidden.
5577      * @param {Number} colIndex The column index
5578      * @param {Boolean} hidden True if the column is hidden
5579      */
5580     setHidden : function(colIndex, hidden){
5581         this.config[colIndex].hidden = hidden;
5582         this.totalWidth = null;
5583         this.fireEvent("hiddenchange", this, colIndex, hidden);
5584     },
5585
5586     /**
5587      * Sets the editor for a column.
5588      * @param {Number} col The column index
5589      * @param {Object} editor The editor object
5590      */
5591     setEditor : function(col, editor){
5592         this.config[col].editor = editor;
5593     }
5594 });
5595
5596 Roo.grid.ColumnModel.defaultRenderer = function(value)
5597 {
5598     if(typeof value == "object") {
5599         return value;
5600     }
5601         if(typeof value == "string" && value.length < 1){
5602             return "&#160;";
5603         }
5604     
5605         return String.format("{0}", value);
5606 };
5607
5608 // Alias for backwards compatibility
5609 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5610 /*
5611  * Based on:
5612  * Ext JS Library 1.1.1
5613  * Copyright(c) 2006-2007, Ext JS, LLC.
5614  *
5615  * Originally Released Under LGPL - original licence link has changed is not relivant.
5616  *
5617  * Fork - LGPL
5618  * <script type="text/javascript">
5619  */
5620  
5621 /**
5622  * @class Roo.LoadMask
5623  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5624  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5625  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5626  * element's UpdateManager load indicator and will be destroyed after the initial load.
5627  * @constructor
5628  * Create a new LoadMask
5629  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5630  * @param {Object} config The config object
5631  */
5632 Roo.LoadMask = function(el, config){
5633     this.el = Roo.get(el);
5634     Roo.apply(this, config);
5635     if(this.store){
5636         this.store.on('beforeload', this.onBeforeLoad, this);
5637         this.store.on('load', this.onLoad, this);
5638         this.store.on('loadexception', this.onLoadException, this);
5639         this.removeMask = false;
5640     }else{
5641         var um = this.el.getUpdateManager();
5642         um.showLoadIndicator = false; // disable the default indicator
5643         um.on('beforeupdate', this.onBeforeLoad, this);
5644         um.on('update', this.onLoad, this);
5645         um.on('failure', this.onLoad, this);
5646         this.removeMask = true;
5647     }
5648 };
5649
5650 Roo.LoadMask.prototype = {
5651     /**
5652      * @cfg {Boolean} removeMask
5653      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5654      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5655      */
5656     /**
5657      * @cfg {String} msg
5658      * The text to display in a centered loading message box (defaults to 'Loading...')
5659      */
5660     msg : 'Loading...',
5661     /**
5662      * @cfg {String} msgCls
5663      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5664      */
5665     msgCls : 'x-mask-loading',
5666
5667     /**
5668      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5669      * @type Boolean
5670      */
5671     disabled: false,
5672
5673     /**
5674      * Disables the mask to prevent it from being displayed
5675      */
5676     disable : function(){
5677        this.disabled = true;
5678     },
5679
5680     /**
5681      * Enables the mask so that it can be displayed
5682      */
5683     enable : function(){
5684         this.disabled = false;
5685     },
5686     
5687     onLoadException : function()
5688     {
5689         Roo.log(arguments);
5690         
5691         if (typeof(arguments[3]) != 'undefined') {
5692             Roo.MessageBox.alert("Error loading",arguments[3]);
5693         } 
5694         /*
5695         try {
5696             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5697                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5698             }   
5699         } catch(e) {
5700             
5701         }
5702         */
5703     
5704         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5705     },
5706     // private
5707     onLoad : function()
5708     {
5709         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5710     },
5711
5712     // private
5713     onBeforeLoad : function(){
5714         if(!this.disabled){
5715             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5716         }
5717     },
5718
5719     // private
5720     destroy : function(){
5721         if(this.store){
5722             this.store.un('beforeload', this.onBeforeLoad, this);
5723             this.store.un('load', this.onLoad, this);
5724             this.store.un('loadexception', this.onLoadException, this);
5725         }else{
5726             var um = this.el.getUpdateManager();
5727             um.un('beforeupdate', this.onBeforeLoad, this);
5728             um.un('update', this.onLoad, this);
5729             um.un('failure', this.onLoad, this);
5730         }
5731     }
5732 };/*
5733  * - LGPL
5734  *
5735  * table
5736  * 
5737  */
5738
5739 /**
5740  * @class Roo.bootstrap.Table
5741  * @extends Roo.bootstrap.Component
5742  * Bootstrap Table class
5743  * @cfg {String} cls table class
5744  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5745  * @cfg {String} bgcolor Specifies the background color for a table
5746  * @cfg {Number} border Specifies whether the table cells should have borders or not
5747  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5748  * @cfg {Number} cellspacing Specifies the space between cells
5749  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5750  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5751  * @cfg {String} sortable Specifies that the table should be sortable
5752  * @cfg {String} summary Specifies a summary of the content of a table
5753  * @cfg {Number} width Specifies the width of a table
5754  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5755  * 
5756  * @cfg {boolean} striped Should the rows be alternative striped
5757  * @cfg {boolean} bordered Add borders to the table
5758  * @cfg {boolean} hover Add hover highlighting
5759  * @cfg {boolean} condensed Format condensed
5760  * @cfg {boolean} responsive Format condensed
5761  * @cfg {Boolean} loadMask (true|false) default false
5762  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5763  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5764  * @cfg {Boolean} rowSelection (true|false) default false
5765  * @cfg {Boolean} cellSelection (true|false) default false
5766  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5767  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5768  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5769  
5770  * 
5771  * @constructor
5772  * Create a new Table
5773  * @param {Object} config The config object
5774  */
5775
5776 Roo.bootstrap.Table = function(config){
5777     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5778     
5779   
5780     
5781     // BC...
5782     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5783     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5784     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5785     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5786     
5787     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5788     if (this.sm) {
5789         this.sm.grid = this;
5790         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5791         this.sm = this.selModel;
5792         this.sm.xmodule = this.xmodule || false;
5793     }
5794     
5795     if (this.cm && typeof(this.cm.config) == 'undefined') {
5796         this.colModel = new Roo.grid.ColumnModel(this.cm);
5797         this.cm = this.colModel;
5798         this.cm.xmodule = this.xmodule || false;
5799     }
5800     if (this.store) {
5801         this.store= Roo.factory(this.store, Roo.data);
5802         this.ds = this.store;
5803         this.ds.xmodule = this.xmodule || false;
5804          
5805     }
5806     if (this.footer && this.store) {
5807         this.footer.dataSource = this.ds;
5808         this.footer = Roo.factory(this.footer);
5809     }
5810     
5811     /** @private */
5812     this.addEvents({
5813         /**
5814          * @event cellclick
5815          * Fires when a cell is clicked
5816          * @param {Roo.bootstrap.Table} this
5817          * @param {Roo.Element} el
5818          * @param {Number} rowIndex
5819          * @param {Number} columnIndex
5820          * @param {Roo.EventObject} e
5821          */
5822         "cellclick" : true,
5823         /**
5824          * @event celldblclick
5825          * Fires when a cell is double clicked
5826          * @param {Roo.bootstrap.Table} this
5827          * @param {Roo.Element} el
5828          * @param {Number} rowIndex
5829          * @param {Number} columnIndex
5830          * @param {Roo.EventObject} e
5831          */
5832         "celldblclick" : true,
5833         /**
5834          * @event rowclick
5835          * Fires when a row is clicked
5836          * @param {Roo.bootstrap.Table} this
5837          * @param {Roo.Element} el
5838          * @param {Number} rowIndex
5839          * @param {Roo.EventObject} e
5840          */
5841         "rowclick" : true,
5842         /**
5843          * @event rowdblclick
5844          * Fires when a row is double clicked
5845          * @param {Roo.bootstrap.Table} this
5846          * @param {Roo.Element} el
5847          * @param {Number} rowIndex
5848          * @param {Roo.EventObject} e
5849          */
5850         "rowdblclick" : true,
5851         /**
5852          * @event mouseover
5853          * Fires when a mouseover occur
5854          * @param {Roo.bootstrap.Table} this
5855          * @param {Roo.Element} el
5856          * @param {Number} rowIndex
5857          * @param {Number} columnIndex
5858          * @param {Roo.EventObject} e
5859          */
5860         "mouseover" : true,
5861         /**
5862          * @event mouseout
5863          * Fires when a mouseout occur
5864          * @param {Roo.bootstrap.Table} this
5865          * @param {Roo.Element} el
5866          * @param {Number} rowIndex
5867          * @param {Number} columnIndex
5868          * @param {Roo.EventObject} e
5869          */
5870         "mouseout" : true,
5871         /**
5872          * @event rowclass
5873          * Fires when a row is rendered, so you can change add a style to it.
5874          * @param {Roo.bootstrap.Table} this
5875          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5876          */
5877         'rowclass' : true,
5878           /**
5879          * @event rowsrendered
5880          * Fires when all the  rows have been rendered
5881          * @param {Roo.bootstrap.Table} this
5882          */
5883         'rowsrendered' : true,
5884         /**
5885          * @event contextmenu
5886          * The raw contextmenu event for the entire grid.
5887          * @param {Roo.EventObject} e
5888          */
5889         "contextmenu" : true,
5890         /**
5891          * @event rowcontextmenu
5892          * Fires when a row is right clicked
5893          * @param {Roo.bootstrap.Table} this
5894          * @param {Number} rowIndex
5895          * @param {Roo.EventObject} e
5896          */
5897         "rowcontextmenu" : true,
5898         /**
5899          * @event cellcontextmenu
5900          * Fires when a cell is right clicked
5901          * @param {Roo.bootstrap.Table} this
5902          * @param {Number} rowIndex
5903          * @param {Number} cellIndex
5904          * @param {Roo.EventObject} e
5905          */
5906          "cellcontextmenu" : true,
5907          /**
5908          * @event headercontextmenu
5909          * Fires when a header is right clicked
5910          * @param {Roo.bootstrap.Table} this
5911          * @param {Number} columnIndex
5912          * @param {Roo.EventObject} e
5913          */
5914         "headercontextmenu" : true
5915     });
5916 };
5917
5918 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5919     
5920     cls: false,
5921     align: false,
5922     bgcolor: false,
5923     border: false,
5924     cellpadding: false,
5925     cellspacing: false,
5926     frame: false,
5927     rules: false,
5928     sortable: false,
5929     summary: false,
5930     width: false,
5931     striped : false,
5932     scrollBody : false,
5933     bordered: false,
5934     hover:  false,
5935     condensed : false,
5936     responsive : false,
5937     sm : false,
5938     cm : false,
5939     store : false,
5940     loadMask : false,
5941     footerShow : true,
5942     headerShow : true,
5943   
5944     rowSelection : false,
5945     cellSelection : false,
5946     layout : false,
5947     
5948     // Roo.Element - the tbody
5949     mainBody: false,
5950     // Roo.Element - thead element
5951     mainHead: false,
5952     
5953     container: false, // used by gridpanel...
5954     
5955     lazyLoad : false,
5956     
5957     getAutoCreate : function()
5958     {
5959         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5960         
5961         cfg = {
5962             tag: 'table',
5963             cls : 'table',
5964             cn : []
5965         };
5966         if (this.scrollBody) {
5967             cfg.cls += ' table-body-fixed';
5968         }    
5969         if (this.striped) {
5970             cfg.cls += ' table-striped';
5971         }
5972         
5973         if (this.hover) {
5974             cfg.cls += ' table-hover';
5975         }
5976         if (this.bordered) {
5977             cfg.cls += ' table-bordered';
5978         }
5979         if (this.condensed) {
5980             cfg.cls += ' table-condensed';
5981         }
5982         if (this.responsive) {
5983             cfg.cls += ' table-responsive';
5984         }
5985         
5986         if (this.cls) {
5987             cfg.cls+=  ' ' +this.cls;
5988         }
5989         
5990         // this lot should be simplifed...
5991         
5992         if (this.align) {
5993             cfg.align=this.align;
5994         }
5995         if (this.bgcolor) {
5996             cfg.bgcolor=this.bgcolor;
5997         }
5998         if (this.border) {
5999             cfg.border=this.border;
6000         }
6001         if (this.cellpadding) {
6002             cfg.cellpadding=this.cellpadding;
6003         }
6004         if (this.cellspacing) {
6005             cfg.cellspacing=this.cellspacing;
6006         }
6007         if (this.frame) {
6008             cfg.frame=this.frame;
6009         }
6010         if (this.rules) {
6011             cfg.rules=this.rules;
6012         }
6013         if (this.sortable) {
6014             cfg.sortable=this.sortable;
6015         }
6016         if (this.summary) {
6017             cfg.summary=this.summary;
6018         }
6019         if (this.width) {
6020             cfg.width=this.width;
6021         }
6022         if (this.layout) {
6023             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6024         }
6025         
6026         if(this.store || this.cm){
6027             if(this.headerShow){
6028                 cfg.cn.push(this.renderHeader());
6029             }
6030             
6031             cfg.cn.push(this.renderBody());
6032             
6033             if(this.footerShow){
6034                 cfg.cn.push(this.renderFooter());
6035             }
6036             // where does this come from?
6037             //cfg.cls+=  ' TableGrid';
6038         }
6039         
6040         return { cn : [ cfg ] };
6041     },
6042     
6043     initEvents : function()
6044     {   
6045         if(!this.store || !this.cm){
6046             return;
6047         }
6048         if (this.selModel) {
6049             this.selModel.initEvents();
6050         }
6051         
6052         
6053         //Roo.log('initEvents with ds!!!!');
6054         
6055         this.mainBody = this.el.select('tbody', true).first();
6056         this.mainHead = this.el.select('thead', true).first();
6057         
6058         
6059         
6060         
6061         var _this = this;
6062         
6063         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6064             e.on('click', _this.sort, _this);
6065         });
6066         
6067         this.mainBody.on("click", this.onClick, this);
6068         this.mainBody.on("dblclick", this.onDblClick, this);
6069         
6070         // why is this done????? = it breaks dialogs??
6071         //this.parent().el.setStyle('position', 'relative');
6072         
6073         
6074         if (this.footer) {
6075             this.footer.parentId = this.id;
6076             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6077             
6078             if(this.lazyLoad){
6079                 this.el.select('tfoot tr td').first().addClass('hide');
6080             }
6081         } 
6082         
6083         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6084         
6085         this.store.on('load', this.onLoad, this);
6086         this.store.on('beforeload', this.onBeforeLoad, this);
6087         this.store.on('update', this.onUpdate, this);
6088         this.store.on('add', this.onAdd, this);
6089         this.store.on("clear", this.clear, this);
6090         
6091         this.el.on("contextmenu", this.onContextMenu, this);
6092         
6093         this.mainBody.on('scroll', this.onBodyScroll, this);
6094         
6095         
6096     },
6097     
6098     onContextMenu : function(e, t)
6099     {
6100         this.processEvent("contextmenu", e);
6101     },
6102     
6103     processEvent : function(name, e)
6104     {
6105         if (name != 'touchstart' ) {
6106             this.fireEvent(name, e);    
6107         }
6108         
6109         var t = e.getTarget();
6110         
6111         var cell = Roo.get(t);
6112         
6113         if(!cell){
6114             return;
6115         }
6116         
6117         if(cell.findParent('tfoot', false, true)){
6118             return;
6119         }
6120         
6121         if(cell.findParent('thead', false, true)){
6122             
6123             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6124                 cell = Roo.get(t).findParent('th', false, true);
6125                 if (!cell) {
6126                     Roo.log("failed to find th in thead?");
6127                     Roo.log(e.getTarget());
6128                     return;
6129                 }
6130             }
6131             
6132             var cellIndex = cell.dom.cellIndex;
6133             
6134             var ename = name == 'touchstart' ? 'click' : name;
6135             this.fireEvent("header" + ename, this, cellIndex, e);
6136             
6137             return;
6138         }
6139         
6140         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6141             cell = Roo.get(t).findParent('td', false, true);
6142             if (!cell) {
6143                 Roo.log("failed to find th in tbody?");
6144                 Roo.log(e.getTarget());
6145                 return;
6146             }
6147         }
6148         
6149         var row = cell.findParent('tr', false, true);
6150         var cellIndex = cell.dom.cellIndex;
6151         var rowIndex = row.dom.rowIndex - 1;
6152         
6153         if(row !== false){
6154             
6155             this.fireEvent("row" + name, this, rowIndex, e);
6156             
6157             if(cell !== false){
6158             
6159                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6160             }
6161         }
6162         
6163     },
6164     
6165     onMouseover : function(e, el)
6166     {
6167         var cell = Roo.get(el);
6168         
6169         if(!cell){
6170             return;
6171         }
6172         
6173         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6174             cell = cell.findParent('td', false, true);
6175         }
6176         
6177         var row = cell.findParent('tr', false, true);
6178         var cellIndex = cell.dom.cellIndex;
6179         var rowIndex = row.dom.rowIndex - 1; // start from 0
6180         
6181         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6182         
6183     },
6184     
6185     onMouseout : function(e, el)
6186     {
6187         var cell = Roo.get(el);
6188         
6189         if(!cell){
6190             return;
6191         }
6192         
6193         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6194             cell = cell.findParent('td', false, true);
6195         }
6196         
6197         var row = cell.findParent('tr', false, true);
6198         var cellIndex = cell.dom.cellIndex;
6199         var rowIndex = row.dom.rowIndex - 1; // start from 0
6200         
6201         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6202         
6203     },
6204     
6205     onClick : function(e, el)
6206     {
6207         var cell = Roo.get(el);
6208         
6209         if(!cell || (!this.cellSelection && !this.rowSelection)){
6210             return;
6211         }
6212         
6213         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6214             cell = cell.findParent('td', false, true);
6215         }
6216         
6217         if(!cell || typeof(cell) == 'undefined'){
6218             return;
6219         }
6220         
6221         var row = cell.findParent('tr', false, true);
6222         
6223         if(!row || typeof(row) == 'undefined'){
6224             return;
6225         }
6226         
6227         var cellIndex = cell.dom.cellIndex;
6228         var rowIndex = this.getRowIndex(row);
6229         
6230         // why??? - should these not be based on SelectionModel?
6231         if(this.cellSelection){
6232             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6233         }
6234         
6235         if(this.rowSelection){
6236             this.fireEvent('rowclick', this, row, rowIndex, e);
6237         }
6238         
6239         
6240     },
6241         
6242     onDblClick : 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         if(this.cellSelection){
6268             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6269         }
6270         
6271         if(this.rowSelection){
6272             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6273         }
6274     },
6275     
6276     sort : function(e,el)
6277     {
6278         var col = Roo.get(el);
6279         
6280         if(!col.hasClass('sortable')){
6281             return;
6282         }
6283         
6284         var sort = col.attr('sort');
6285         var dir = 'ASC';
6286         
6287         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6288             dir = 'DESC';
6289         }
6290         
6291         this.store.sortInfo = {field : sort, direction : dir};
6292         
6293         if (this.footer) {
6294             Roo.log("calling footer first");
6295             this.footer.onClick('first');
6296         } else {
6297         
6298             this.store.load({ params : { start : 0 } });
6299         }
6300     },
6301     
6302     renderHeader : function()
6303     {
6304         var header = {
6305             tag: 'thead',
6306             cn : []
6307         };
6308         
6309         var cm = this.cm;
6310         this.totalWidth = 0;
6311         
6312         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6313             
6314             var config = cm.config[i];
6315             
6316             var c = {
6317                 tag: 'th',
6318                 style : '',
6319                 html: cm.getColumnHeader(i)
6320             };
6321             
6322             var hh = '';
6323             
6324             if(typeof(config.sortable) != 'undefined' && config.sortable){
6325                 c.cls = 'sortable';
6326                 c.html = '<i class="glyphicon"></i>' + c.html;
6327             }
6328             
6329             if(typeof(config.lgHeader) != 'undefined'){
6330                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6331             }
6332             
6333             if(typeof(config.mdHeader) != 'undefined'){
6334                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6335             }
6336             
6337             if(typeof(config.smHeader) != 'undefined'){
6338                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6339             }
6340             
6341             if(typeof(config.xsHeader) != 'undefined'){
6342                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6343             }
6344             
6345             if(hh.length){
6346                 c.html = hh;
6347             }
6348             
6349             if(typeof(config.tooltip) != 'undefined'){
6350                 c.tooltip = config.tooltip;
6351             }
6352             
6353             if(typeof(config.colspan) != 'undefined'){
6354                 c.colspan = config.colspan;
6355             }
6356             
6357             if(typeof(config.hidden) != 'undefined' && config.hidden){
6358                 c.style += ' display:none;';
6359             }
6360             
6361             if(typeof(config.dataIndex) != 'undefined'){
6362                 c.sort = config.dataIndex;
6363             }
6364             
6365            
6366             
6367             if(typeof(config.align) != 'undefined' && config.align.length){
6368                 c.style += ' text-align:' + config.align + ';';
6369             }
6370             
6371             if(typeof(config.width) != 'undefined'){
6372                 c.style += ' width:' + config.width + 'px;';
6373                 this.totalWidth += config.width;
6374             } else {
6375                 this.totalWidth += 100; // assume minimum of 100 per column?
6376             }
6377             
6378             if(typeof(config.cls) != 'undefined'){
6379                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6380             }
6381             
6382             ['xs','sm','md','lg'].map(function(size){
6383                 
6384                 if(typeof(config[size]) == 'undefined'){
6385                     return;
6386                 }
6387                 
6388                 if (!config[size]) { // 0 = hidden
6389                     c.cls += ' hidden-' + size;
6390                     return;
6391                 }
6392                 
6393                 c.cls += ' col-' + size + '-' + config[size];
6394
6395             });
6396             
6397             header.cn.push(c)
6398         }
6399         
6400         return header;
6401     },
6402     
6403     renderBody : function()
6404     {
6405         var body = {
6406             tag: 'tbody',
6407             cn : [
6408                 {
6409                     tag: 'tr',
6410                     cn : [
6411                         {
6412                             tag : 'td',
6413                             colspan :  this.cm.getColumnCount()
6414                         }
6415                     ]
6416                 }
6417             ]
6418         };
6419         
6420         return body;
6421     },
6422     
6423     renderFooter : function()
6424     {
6425         var footer = {
6426             tag: 'tfoot',
6427             cn : [
6428                 {
6429                     tag: 'tr',
6430                     cn : [
6431                         {
6432                             tag : 'td',
6433                             colspan :  this.cm.getColumnCount()
6434                         }
6435                     ]
6436                 }
6437             ]
6438         };
6439         
6440         return footer;
6441     },
6442     
6443     
6444     
6445     onLoad : function()
6446     {
6447 //        Roo.log('ds onload');
6448         this.clear();
6449         
6450         var _this = this;
6451         var cm = this.cm;
6452         var ds = this.store;
6453         
6454         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6455             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6456             if (_this.store.sortInfo) {
6457                     
6458                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6459                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6460                 }
6461                 
6462                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6463                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6464                 }
6465             }
6466         });
6467         
6468         var tbody =  this.mainBody;
6469               
6470         if(ds.getCount() > 0){
6471             ds.data.each(function(d,rowIndex){
6472                 var row =  this.renderRow(cm, ds, rowIndex);
6473                 
6474                 tbody.createChild(row);
6475                 
6476                 var _this = this;
6477                 
6478                 if(row.cellObjects.length){
6479                     Roo.each(row.cellObjects, function(r){
6480                         _this.renderCellObject(r);
6481                     })
6482                 }
6483                 
6484             }, this);
6485         }
6486         
6487         Roo.each(this.el.select('tbody td', true).elements, function(e){
6488             e.on('mouseover', _this.onMouseover, _this);
6489         });
6490         
6491         Roo.each(this.el.select('tbody td', true).elements, function(e){
6492             e.on('mouseout', _this.onMouseout, _this);
6493         });
6494         this.fireEvent('rowsrendered', this);
6495         //if(this.loadMask){
6496         //    this.maskEl.hide();
6497         //}
6498         
6499         this.autoSize();
6500     },
6501     
6502     
6503     onUpdate : function(ds,record)
6504     {
6505         this.refreshRow(record);
6506         this.autoSize();
6507     },
6508     
6509     onRemove : function(ds, record, index, isUpdate){
6510         if(isUpdate !== true){
6511             this.fireEvent("beforerowremoved", this, index, record);
6512         }
6513         var bt = this.mainBody.dom;
6514         
6515         var rows = this.el.select('tbody > tr', true).elements;
6516         
6517         if(typeof(rows[index]) != 'undefined'){
6518             bt.removeChild(rows[index].dom);
6519         }
6520         
6521 //        if(bt.rows[index]){
6522 //            bt.removeChild(bt.rows[index]);
6523 //        }
6524         
6525         if(isUpdate !== true){
6526             //this.stripeRows(index);
6527             //this.syncRowHeights(index, index);
6528             //this.layout();
6529             this.fireEvent("rowremoved", this, index, record);
6530         }
6531     },
6532     
6533     onAdd : function(ds, records, rowIndex)
6534     {
6535         //Roo.log('on Add called');
6536         // - note this does not handle multiple adding very well..
6537         var bt = this.mainBody.dom;
6538         for (var i =0 ; i < records.length;i++) {
6539             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6540             //Roo.log(records[i]);
6541             //Roo.log(this.store.getAt(rowIndex+i));
6542             this.insertRow(this.store, rowIndex + i, false);
6543             return;
6544         }
6545         
6546     },
6547     
6548     
6549     refreshRow : function(record){
6550         var ds = this.store, index;
6551         if(typeof record == 'number'){
6552             index = record;
6553             record = ds.getAt(index);
6554         }else{
6555             index = ds.indexOf(record);
6556         }
6557         this.insertRow(ds, index, true);
6558         this.autoSize();
6559         this.onRemove(ds, record, index+1, true);
6560         this.autoSize();
6561         //this.syncRowHeights(index, index);
6562         //this.layout();
6563         this.fireEvent("rowupdated", this, index, record);
6564     },
6565     
6566     insertRow : function(dm, rowIndex, isUpdate){
6567         
6568         if(!isUpdate){
6569             this.fireEvent("beforerowsinserted", this, rowIndex);
6570         }
6571             //var s = this.getScrollState();
6572         var row = this.renderRow(this.cm, this.store, rowIndex);
6573         // insert before rowIndex..
6574         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6575         
6576         var _this = this;
6577                 
6578         if(row.cellObjects.length){
6579             Roo.each(row.cellObjects, function(r){
6580                 _this.renderCellObject(r);
6581             })
6582         }
6583             
6584         if(!isUpdate){
6585             this.fireEvent("rowsinserted", this, rowIndex);
6586             //this.syncRowHeights(firstRow, lastRow);
6587             //this.stripeRows(firstRow);
6588             //this.layout();
6589         }
6590         
6591     },
6592     
6593     
6594     getRowDom : function(rowIndex)
6595     {
6596         var rows = this.el.select('tbody > tr', true).elements;
6597         
6598         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6599         
6600     },
6601     // returns the object tree for a tr..
6602   
6603     
6604     renderRow : function(cm, ds, rowIndex) 
6605     {
6606         
6607         var d = ds.getAt(rowIndex);
6608         
6609         var row = {
6610             tag : 'tr',
6611             cn : []
6612         };
6613             
6614         var cellObjects = [];
6615         
6616         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6617             var config = cm.config[i];
6618             
6619             var renderer = cm.getRenderer(i);
6620             var value = '';
6621             var id = false;
6622             
6623             if(typeof(renderer) !== 'undefined'){
6624                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6625             }
6626             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6627             // and are rendered into the cells after the row is rendered - using the id for the element.
6628             
6629             if(typeof(value) === 'object'){
6630                 id = Roo.id();
6631                 cellObjects.push({
6632                     container : id,
6633                     cfg : value 
6634                 })
6635             }
6636             
6637             var rowcfg = {
6638                 record: d,
6639                 rowIndex : rowIndex,
6640                 colIndex : i,
6641                 rowClass : ''
6642             };
6643
6644             this.fireEvent('rowclass', this, rowcfg);
6645             
6646             var td = {
6647                 tag: 'td',
6648                 cls : rowcfg.rowClass,
6649                 style: '',
6650                 html: (typeof(value) === 'object') ? '' : value
6651             };
6652             
6653             if (id) {
6654                 td.id = id;
6655             }
6656             
6657             if(typeof(config.colspan) != 'undefined'){
6658                 td.colspan = config.colspan;
6659             }
6660             
6661             if(typeof(config.hidden) != 'undefined' && config.hidden){
6662                 td.style += ' display:none;';
6663             }
6664             
6665             if(typeof(config.align) != 'undefined' && config.align.length){
6666                 td.style += ' text-align:' + config.align + ';';
6667             }
6668             
6669             if(typeof(config.width) != 'undefined'){
6670                 td.style += ' width:' +  config.width + 'px;';
6671             }
6672             
6673             if(typeof(config.cursor) != 'undefined'){
6674                 td.style += ' cursor:' +  config.cursor + ';';
6675             }
6676             
6677             if(typeof(config.cls) != 'undefined'){
6678                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6679             }
6680             
6681             ['xs','sm','md','lg'].map(function(size){
6682                 
6683                 if(typeof(config[size]) == 'undefined'){
6684                     return;
6685                 }
6686                 
6687                 if (!config[size]) { // 0 = hidden
6688                     td.cls += ' hidden-' + size;
6689                     return;
6690                 }
6691                 
6692                 td.cls += ' col-' + size + '-' + config[size];
6693
6694             });
6695              
6696             row.cn.push(td);
6697            
6698         }
6699         
6700         row.cellObjects = cellObjects;
6701         
6702         return row;
6703           
6704     },
6705     
6706     
6707     
6708     onBeforeLoad : function()
6709     {
6710         //Roo.log('ds onBeforeLoad');
6711         
6712         //this.clear();
6713         
6714         //if(this.loadMask){
6715         //    this.maskEl.show();
6716         //}
6717     },
6718      /**
6719      * Remove all rows
6720      */
6721     clear : function()
6722     {
6723         this.el.select('tbody', true).first().dom.innerHTML = '';
6724     },
6725     /**
6726      * Show or hide a row.
6727      * @param {Number} rowIndex to show or hide
6728      * @param {Boolean} state hide
6729      */
6730     setRowVisibility : function(rowIndex, state)
6731     {
6732         var bt = this.mainBody.dom;
6733         
6734         var rows = this.el.select('tbody > tr', true).elements;
6735         
6736         if(typeof(rows[rowIndex]) == 'undefined'){
6737             return;
6738         }
6739         rows[rowIndex].dom.style.display = state ? '' : 'none';
6740     },
6741     
6742     
6743     getSelectionModel : function(){
6744         if(!this.selModel){
6745             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6746         }
6747         return this.selModel;
6748     },
6749     /*
6750      * Render the Roo.bootstrap object from renderder
6751      */
6752     renderCellObject : function(r)
6753     {
6754         var _this = this;
6755         
6756         var t = r.cfg.render(r.container);
6757         
6758         if(r.cfg.cn){
6759             Roo.each(r.cfg.cn, function(c){
6760                 var child = {
6761                     container: t.getChildContainer(),
6762                     cfg: c
6763                 };
6764                 _this.renderCellObject(child);
6765             })
6766         }
6767     },
6768     
6769     getRowIndex : function(row)
6770     {
6771         var rowIndex = -1;
6772         
6773         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6774             if(el != row){
6775                 return;
6776             }
6777             
6778             rowIndex = index;
6779         });
6780         
6781         return rowIndex;
6782     },
6783      /**
6784      * Returns the grid's underlying element = used by panel.Grid
6785      * @return {Element} The element
6786      */
6787     getGridEl : function(){
6788         return this.el;
6789     },
6790      /**
6791      * Forces a resize - used by panel.Grid
6792      * @return {Element} The element
6793      */
6794     autoSize : function()
6795     {
6796         //var ctr = Roo.get(this.container.dom.parentElement);
6797         var ctr = Roo.get(this.el.dom);
6798         
6799         var thd = this.getGridEl().select('thead',true).first();
6800         var tbd = this.getGridEl().select('tbody', true).first();
6801         var tfd = this.getGridEl().select('tfoot', true).first();
6802         
6803         var cw = ctr.getWidth();
6804         
6805         if (tbd) {
6806             
6807             tbd.setSize(ctr.getWidth(),
6808                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6809             );
6810             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6811             cw -= barsize;
6812         }
6813         cw = Math.max(cw, this.totalWidth);
6814         this.getGridEl().select('tr',true).setWidth(cw);
6815         // resize 'expandable coloumn?
6816         
6817         return; // we doe not have a view in this design..
6818         
6819     },
6820     onBodyScroll: function()
6821     {
6822         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6823         this.mainHead.setStyle({
6824             'position' : 'relative',
6825             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6826         });
6827         
6828         if(this.lazyLoad){
6829             
6830             var scrollHeight = this.mainBody.dom.scrollHeight;
6831             
6832             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6833             
6834             var height = this.mainBody.getHeight();
6835             
6836             if(scrollHeight - height == scrollTop) {
6837                 
6838                 var total = this.ds.getTotalCount();
6839                 
6840                 if(this.footer.cursor + this.footer.pageSize < total){
6841                     
6842                     this.footer.ds.load({
6843                         params : {
6844                             start : this.footer.cursor + this.footer.pageSize,
6845                             limit : this.footer.pageSize
6846                         },
6847                         add : true
6848                     });
6849                 }
6850             }
6851             
6852         }
6853     }
6854 });
6855
6856  
6857
6858  /*
6859  * - LGPL
6860  *
6861  * table cell
6862  * 
6863  */
6864
6865 /**
6866  * @class Roo.bootstrap.TableCell
6867  * @extends Roo.bootstrap.Component
6868  * Bootstrap TableCell class
6869  * @cfg {String} html cell contain text
6870  * @cfg {String} cls cell class
6871  * @cfg {String} tag cell tag (td|th) default td
6872  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6873  * @cfg {String} align Aligns the content in a cell
6874  * @cfg {String} axis Categorizes cells
6875  * @cfg {String} bgcolor Specifies the background color of a cell
6876  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6877  * @cfg {Number} colspan Specifies the number of columns a cell should span
6878  * @cfg {String} headers Specifies one or more header cells a cell is related to
6879  * @cfg {Number} height Sets the height of a cell
6880  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6881  * @cfg {Number} rowspan Sets the number of rows a cell should span
6882  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6883  * @cfg {String} valign Vertical aligns the content in a cell
6884  * @cfg {Number} width Specifies the width of a cell
6885  * 
6886  * @constructor
6887  * Create a new TableCell
6888  * @param {Object} config The config object
6889  */
6890
6891 Roo.bootstrap.TableCell = function(config){
6892     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6893 };
6894
6895 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6896     
6897     html: false,
6898     cls: false,
6899     tag: false,
6900     abbr: false,
6901     align: false,
6902     axis: false,
6903     bgcolor: false,
6904     charoff: false,
6905     colspan: false,
6906     headers: false,
6907     height: false,
6908     nowrap: false,
6909     rowspan: false,
6910     scope: false,
6911     valign: false,
6912     width: false,
6913     
6914     
6915     getAutoCreate : function(){
6916         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6917         
6918         cfg = {
6919             tag: 'td'
6920         };
6921         
6922         if(this.tag){
6923             cfg.tag = this.tag;
6924         }
6925         
6926         if (this.html) {
6927             cfg.html=this.html
6928         }
6929         if (this.cls) {
6930             cfg.cls=this.cls
6931         }
6932         if (this.abbr) {
6933             cfg.abbr=this.abbr
6934         }
6935         if (this.align) {
6936             cfg.align=this.align
6937         }
6938         if (this.axis) {
6939             cfg.axis=this.axis
6940         }
6941         if (this.bgcolor) {
6942             cfg.bgcolor=this.bgcolor
6943         }
6944         if (this.charoff) {
6945             cfg.charoff=this.charoff
6946         }
6947         if (this.colspan) {
6948             cfg.colspan=this.colspan
6949         }
6950         if (this.headers) {
6951             cfg.headers=this.headers
6952         }
6953         if (this.height) {
6954             cfg.height=this.height
6955         }
6956         if (this.nowrap) {
6957             cfg.nowrap=this.nowrap
6958         }
6959         if (this.rowspan) {
6960             cfg.rowspan=this.rowspan
6961         }
6962         if (this.scope) {
6963             cfg.scope=this.scope
6964         }
6965         if (this.valign) {
6966             cfg.valign=this.valign
6967         }
6968         if (this.width) {
6969             cfg.width=this.width
6970         }
6971         
6972         
6973         return cfg;
6974     }
6975    
6976 });
6977
6978  
6979
6980  /*
6981  * - LGPL
6982  *
6983  * table row
6984  * 
6985  */
6986
6987 /**
6988  * @class Roo.bootstrap.TableRow
6989  * @extends Roo.bootstrap.Component
6990  * Bootstrap TableRow class
6991  * @cfg {String} cls row class
6992  * @cfg {String} align Aligns the content in a table row
6993  * @cfg {String} bgcolor Specifies a background color for a table row
6994  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6995  * @cfg {String} valign Vertical aligns the content in a table row
6996  * 
6997  * @constructor
6998  * Create a new TableRow
6999  * @param {Object} config The config object
7000  */
7001
7002 Roo.bootstrap.TableRow = function(config){
7003     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7004 };
7005
7006 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7007     
7008     cls: false,
7009     align: false,
7010     bgcolor: false,
7011     charoff: false,
7012     valign: false,
7013     
7014     getAutoCreate : function(){
7015         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7016         
7017         cfg = {
7018             tag: 'tr'
7019         };
7020             
7021         if(this.cls){
7022             cfg.cls = this.cls;
7023         }
7024         if(this.align){
7025             cfg.align = this.align;
7026         }
7027         if(this.bgcolor){
7028             cfg.bgcolor = this.bgcolor;
7029         }
7030         if(this.charoff){
7031             cfg.charoff = this.charoff;
7032         }
7033         if(this.valign){
7034             cfg.valign = this.valign;
7035         }
7036         
7037         return cfg;
7038     }
7039    
7040 });
7041
7042  
7043
7044  /*
7045  * - LGPL
7046  *
7047  * table body
7048  * 
7049  */
7050
7051 /**
7052  * @class Roo.bootstrap.TableBody
7053  * @extends Roo.bootstrap.Component
7054  * Bootstrap TableBody class
7055  * @cfg {String} cls element class
7056  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7057  * @cfg {String} align Aligns the content inside the element
7058  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7059  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7060  * 
7061  * @constructor
7062  * Create a new TableBody
7063  * @param {Object} config The config object
7064  */
7065
7066 Roo.bootstrap.TableBody = function(config){
7067     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7068 };
7069
7070 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7071     
7072     cls: false,
7073     tag: false,
7074     align: false,
7075     charoff: false,
7076     valign: false,
7077     
7078     getAutoCreate : function(){
7079         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7080         
7081         cfg = {
7082             tag: 'tbody'
7083         };
7084             
7085         if (this.cls) {
7086             cfg.cls=this.cls
7087         }
7088         if(this.tag){
7089             cfg.tag = this.tag;
7090         }
7091         
7092         if(this.align){
7093             cfg.align = this.align;
7094         }
7095         if(this.charoff){
7096             cfg.charoff = this.charoff;
7097         }
7098         if(this.valign){
7099             cfg.valign = this.valign;
7100         }
7101         
7102         return cfg;
7103     }
7104     
7105     
7106 //    initEvents : function()
7107 //    {
7108 //        
7109 //        if(!this.store){
7110 //            return;
7111 //        }
7112 //        
7113 //        this.store = Roo.factory(this.store, Roo.data);
7114 //        this.store.on('load', this.onLoad, this);
7115 //        
7116 //        this.store.load();
7117 //        
7118 //    },
7119 //    
7120 //    onLoad: function () 
7121 //    {   
7122 //        this.fireEvent('load', this);
7123 //    }
7124 //    
7125 //   
7126 });
7127
7128  
7129
7130  /*
7131  * Based on:
7132  * Ext JS Library 1.1.1
7133  * Copyright(c) 2006-2007, Ext JS, LLC.
7134  *
7135  * Originally Released Under LGPL - original licence link has changed is not relivant.
7136  *
7137  * Fork - LGPL
7138  * <script type="text/javascript">
7139  */
7140
7141 // as we use this in bootstrap.
7142 Roo.namespace('Roo.form');
7143  /**
7144  * @class Roo.form.Action
7145  * Internal Class used to handle form actions
7146  * @constructor
7147  * @param {Roo.form.BasicForm} el The form element or its id
7148  * @param {Object} config Configuration options
7149  */
7150
7151  
7152  
7153 // define the action interface
7154 Roo.form.Action = function(form, options){
7155     this.form = form;
7156     this.options = options || {};
7157 };
7158 /**
7159  * Client Validation Failed
7160  * @const 
7161  */
7162 Roo.form.Action.CLIENT_INVALID = 'client';
7163 /**
7164  * Server Validation Failed
7165  * @const 
7166  */
7167 Roo.form.Action.SERVER_INVALID = 'server';
7168  /**
7169  * Connect to Server Failed
7170  * @const 
7171  */
7172 Roo.form.Action.CONNECT_FAILURE = 'connect';
7173 /**
7174  * Reading Data from Server Failed
7175  * @const 
7176  */
7177 Roo.form.Action.LOAD_FAILURE = 'load';
7178
7179 Roo.form.Action.prototype = {
7180     type : 'default',
7181     failureType : undefined,
7182     response : undefined,
7183     result : undefined,
7184
7185     // interface method
7186     run : function(options){
7187
7188     },
7189
7190     // interface method
7191     success : function(response){
7192
7193     },
7194
7195     // interface method
7196     handleResponse : function(response){
7197
7198     },
7199
7200     // default connection failure
7201     failure : function(response){
7202         
7203         this.response = response;
7204         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7205         this.form.afterAction(this, false);
7206     },
7207
7208     processResponse : function(response){
7209         this.response = response;
7210         if(!response.responseText){
7211             return true;
7212         }
7213         this.result = this.handleResponse(response);
7214         return this.result;
7215     },
7216
7217     // utility functions used internally
7218     getUrl : function(appendParams){
7219         var url = this.options.url || this.form.url || this.form.el.dom.action;
7220         if(appendParams){
7221             var p = this.getParams();
7222             if(p){
7223                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7224             }
7225         }
7226         return url;
7227     },
7228
7229     getMethod : function(){
7230         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7231     },
7232
7233     getParams : function(){
7234         var bp = this.form.baseParams;
7235         var p = this.options.params;
7236         if(p){
7237             if(typeof p == "object"){
7238                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7239             }else if(typeof p == 'string' && bp){
7240                 p += '&' + Roo.urlEncode(bp);
7241             }
7242         }else if(bp){
7243             p = Roo.urlEncode(bp);
7244         }
7245         return p;
7246     },
7247
7248     createCallback : function(){
7249         return {
7250             success: this.success,
7251             failure: this.failure,
7252             scope: this,
7253             timeout: (this.form.timeout*1000),
7254             upload: this.form.fileUpload ? this.success : undefined
7255         };
7256     }
7257 };
7258
7259 Roo.form.Action.Submit = function(form, options){
7260     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7261 };
7262
7263 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7264     type : 'submit',
7265
7266     haveProgress : false,
7267     uploadComplete : false,
7268     
7269     // uploadProgress indicator.
7270     uploadProgress : function()
7271     {
7272         if (!this.form.progressUrl) {
7273             return;
7274         }
7275         
7276         if (!this.haveProgress) {
7277             Roo.MessageBox.progress("Uploading", "Uploading");
7278         }
7279         if (this.uploadComplete) {
7280            Roo.MessageBox.hide();
7281            return;
7282         }
7283         
7284         this.haveProgress = true;
7285    
7286         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7287         
7288         var c = new Roo.data.Connection();
7289         c.request({
7290             url : this.form.progressUrl,
7291             params: {
7292                 id : uid
7293             },
7294             method: 'GET',
7295             success : function(req){
7296                //console.log(data);
7297                 var rdata = false;
7298                 var edata;
7299                 try  {
7300                    rdata = Roo.decode(req.responseText)
7301                 } catch (e) {
7302                     Roo.log("Invalid data from server..");
7303                     Roo.log(edata);
7304                     return;
7305                 }
7306                 if (!rdata || !rdata.success) {
7307                     Roo.log(rdata);
7308                     Roo.MessageBox.alert(Roo.encode(rdata));
7309                     return;
7310                 }
7311                 var data = rdata.data;
7312                 
7313                 if (this.uploadComplete) {
7314                    Roo.MessageBox.hide();
7315                    return;
7316                 }
7317                    
7318                 if (data){
7319                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7320                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7321                     );
7322                 }
7323                 this.uploadProgress.defer(2000,this);
7324             },
7325        
7326             failure: function(data) {
7327                 Roo.log('progress url failed ');
7328                 Roo.log(data);
7329             },
7330             scope : this
7331         });
7332            
7333     },
7334     
7335     
7336     run : function()
7337     {
7338         // run get Values on the form, so it syncs any secondary forms.
7339         this.form.getValues();
7340         
7341         var o = this.options;
7342         var method = this.getMethod();
7343         var isPost = method == 'POST';
7344         if(o.clientValidation === false || this.form.isValid()){
7345             
7346             if (this.form.progressUrl) {
7347                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7348                     (new Date() * 1) + '' + Math.random());
7349                     
7350             } 
7351             
7352             
7353             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7354                 form:this.form.el.dom,
7355                 url:this.getUrl(!isPost),
7356                 method: method,
7357                 params:isPost ? this.getParams() : null,
7358                 isUpload: this.form.fileUpload
7359             }));
7360             
7361             this.uploadProgress();
7362
7363         }else if (o.clientValidation !== false){ // client validation failed
7364             this.failureType = Roo.form.Action.CLIENT_INVALID;
7365             this.form.afterAction(this, false);
7366         }
7367     },
7368
7369     success : function(response)
7370     {
7371         this.uploadComplete= true;
7372         if (this.haveProgress) {
7373             Roo.MessageBox.hide();
7374         }
7375         
7376         
7377         var result = this.processResponse(response);
7378         if(result === true || result.success){
7379             this.form.afterAction(this, true);
7380             return;
7381         }
7382         if(result.errors){
7383             this.form.markInvalid(result.errors);
7384             this.failureType = Roo.form.Action.SERVER_INVALID;
7385         }
7386         this.form.afterAction(this, false);
7387     },
7388     failure : function(response)
7389     {
7390         this.uploadComplete= true;
7391         if (this.haveProgress) {
7392             Roo.MessageBox.hide();
7393         }
7394         
7395         this.response = response;
7396         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7397         this.form.afterAction(this, false);
7398     },
7399     
7400     handleResponse : function(response){
7401         if(this.form.errorReader){
7402             var rs = this.form.errorReader.read(response);
7403             var errors = [];
7404             if(rs.records){
7405                 for(var i = 0, len = rs.records.length; i < len; i++) {
7406                     var r = rs.records[i];
7407                     errors[i] = r.data;
7408                 }
7409             }
7410             if(errors.length < 1){
7411                 errors = null;
7412             }
7413             return {
7414                 success : rs.success,
7415                 errors : errors
7416             };
7417         }
7418         var ret = false;
7419         try {
7420             ret = Roo.decode(response.responseText);
7421         } catch (e) {
7422             ret = {
7423                 success: false,
7424                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7425                 errors : []
7426             };
7427         }
7428         return ret;
7429         
7430     }
7431 });
7432
7433
7434 Roo.form.Action.Load = function(form, options){
7435     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7436     this.reader = this.form.reader;
7437 };
7438
7439 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7440     type : 'load',
7441
7442     run : function(){
7443         
7444         Roo.Ajax.request(Roo.apply(
7445                 this.createCallback(), {
7446                     method:this.getMethod(),
7447                     url:this.getUrl(false),
7448                     params:this.getParams()
7449         }));
7450     },
7451
7452     success : function(response){
7453         
7454         var result = this.processResponse(response);
7455         if(result === true || !result.success || !result.data){
7456             this.failureType = Roo.form.Action.LOAD_FAILURE;
7457             this.form.afterAction(this, false);
7458             return;
7459         }
7460         this.form.clearInvalid();
7461         this.form.setValues(result.data);
7462         this.form.afterAction(this, true);
7463     },
7464
7465     handleResponse : function(response){
7466         if(this.form.reader){
7467             var rs = this.form.reader.read(response);
7468             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7469             return {
7470                 success : rs.success,
7471                 data : data
7472             };
7473         }
7474         return Roo.decode(response.responseText);
7475     }
7476 });
7477
7478 Roo.form.Action.ACTION_TYPES = {
7479     'load' : Roo.form.Action.Load,
7480     'submit' : Roo.form.Action.Submit
7481 };/*
7482  * - LGPL
7483  *
7484  * form
7485  *
7486  */
7487
7488 /**
7489  * @class Roo.bootstrap.Form
7490  * @extends Roo.bootstrap.Component
7491  * Bootstrap Form class
7492  * @cfg {String} method  GET | POST (default POST)
7493  * @cfg {String} labelAlign top | left (default top)
7494  * @cfg {String} align left  | right - for navbars
7495  * @cfg {Boolean} loadMask load mask when submit (default true)
7496
7497  *
7498  * @constructor
7499  * Create a new Form
7500  * @param {Object} config The config object
7501  */
7502
7503
7504 Roo.bootstrap.Form = function(config){
7505     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7506     
7507     Roo.bootstrap.Form.popover.apply();
7508     
7509     this.addEvents({
7510         /**
7511          * @event clientvalidation
7512          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7513          * @param {Form} this
7514          * @param {Boolean} valid true if the form has passed client-side validation
7515          */
7516         clientvalidation: true,
7517         /**
7518          * @event beforeaction
7519          * Fires before any action is performed. Return false to cancel the action.
7520          * @param {Form} this
7521          * @param {Action} action The action to be performed
7522          */
7523         beforeaction: true,
7524         /**
7525          * @event actionfailed
7526          * Fires when an action fails.
7527          * @param {Form} this
7528          * @param {Action} action The action that failed
7529          */
7530         actionfailed : true,
7531         /**
7532          * @event actioncomplete
7533          * Fires when an action is completed.
7534          * @param {Form} this
7535          * @param {Action} action The action that completed
7536          */
7537         actioncomplete : true
7538     });
7539
7540 };
7541
7542 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7543
7544      /**
7545      * @cfg {String} method
7546      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7547      */
7548     method : 'POST',
7549     /**
7550      * @cfg {String} url
7551      * The URL to use for form actions if one isn't supplied in the action options.
7552      */
7553     /**
7554      * @cfg {Boolean} fileUpload
7555      * Set to true if this form is a file upload.
7556      */
7557
7558     /**
7559      * @cfg {Object} baseParams
7560      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7561      */
7562
7563     /**
7564      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7565      */
7566     timeout: 30,
7567     /**
7568      * @cfg {Sting} align (left|right) for navbar forms
7569      */
7570     align : 'left',
7571
7572     // private
7573     activeAction : null,
7574
7575     /**
7576      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7577      * element by passing it or its id or mask the form itself by passing in true.
7578      * @type Mixed
7579      */
7580     waitMsgTarget : false,
7581
7582     loadMask : true,
7583     
7584     /**
7585      * @cfg {Boolean} errorMask (true|false) default false
7586      */
7587     errorMask : false,
7588     
7589     /**
7590      * @cfg {Number} maskOffset Default 100
7591      */
7592     maskOffset : 100,
7593
7594     getAutoCreate : function(){
7595
7596         var cfg = {
7597             tag: 'form',
7598             method : this.method || 'POST',
7599             id : this.id || Roo.id(),
7600             cls : ''
7601         };
7602         if (this.parent().xtype.match(/^Nav/)) {
7603             cfg.cls = 'navbar-form navbar-' + this.align;
7604
7605         }
7606
7607         if (this.labelAlign == 'left' ) {
7608             cfg.cls += ' form-horizontal';
7609         }
7610
7611
7612         return cfg;
7613     },
7614     initEvents : function()
7615     {
7616         this.el.on('submit', this.onSubmit, this);
7617         // this was added as random key presses on the form where triggering form submit.
7618         this.el.on('keypress', function(e) {
7619             if (e.getCharCode() != 13) {
7620                 return true;
7621             }
7622             // we might need to allow it for textareas.. and some other items.
7623             // check e.getTarget().
7624
7625             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7626                 return true;
7627             }
7628
7629             Roo.log("keypress blocked");
7630
7631             e.preventDefault();
7632             return false;
7633         });
7634         
7635     },
7636     // private
7637     onSubmit : function(e){
7638         e.stopEvent();
7639     },
7640
7641      /**
7642      * Returns true if client-side validation on the form is successful.
7643      * @return Boolean
7644      */
7645     isValid : function(){
7646         var items = this.getItems();
7647         var valid = true;
7648         var target = false;
7649         
7650         items.each(function(f){
7651             
7652             if(!f.validate()) {
7653                 Roo.log(f.name);
7654             }
7655             
7656             if(f.validate()){
7657                 return;
7658             }
7659             valid = false;
7660
7661             if(!target && f.el.isVisible(true)){
7662                 target = f;
7663             }
7664            
7665         });
7666         
7667         if(this.errorMask && !valid){
7668             Roo.bootstrap.Form.popover.mask(this, target);
7669         }
7670         
7671         return valid;
7672     },
7673     
7674     /**
7675      * Returns true if any fields in this form have changed since their original load.
7676      * @return Boolean
7677      */
7678     isDirty : function(){
7679         var dirty = false;
7680         var items = this.getItems();
7681         items.each(function(f){
7682            if(f.isDirty()){
7683                dirty = true;
7684                return false;
7685            }
7686            return true;
7687         });
7688         return dirty;
7689     },
7690      /**
7691      * Performs a predefined action (submit or load) or custom actions you define on this form.
7692      * @param {String} actionName The name of the action type
7693      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7694      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7695      * accept other config options):
7696      * <pre>
7697 Property          Type             Description
7698 ----------------  ---------------  ----------------------------------------------------------------------------------
7699 url               String           The url for the action (defaults to the form's url)
7700 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7701 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7702 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7703                                    validate the form on the client (defaults to false)
7704      * </pre>
7705      * @return {BasicForm} this
7706      */
7707     doAction : function(action, options){
7708         if(typeof action == 'string'){
7709             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7710         }
7711         if(this.fireEvent('beforeaction', this, action) !== false){
7712             this.beforeAction(action);
7713             action.run.defer(100, action);
7714         }
7715         return this;
7716     },
7717
7718     // private
7719     beforeAction : function(action){
7720         var o = action.options;
7721
7722         if(this.loadMask){
7723             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7724         }
7725         // not really supported yet.. ??
7726
7727         //if(this.waitMsgTarget === true){
7728         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7729         //}else if(this.waitMsgTarget){
7730         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7731         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7732         //}else {
7733         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7734        // }
7735
7736     },
7737
7738     // private
7739     afterAction : function(action, success){
7740         this.activeAction = null;
7741         var o = action.options;
7742
7743         //if(this.waitMsgTarget === true){
7744             this.el.unmask();
7745         //}else if(this.waitMsgTarget){
7746         //    this.waitMsgTarget.unmask();
7747         //}else{
7748         //    Roo.MessageBox.updateProgress(1);
7749         //    Roo.MessageBox.hide();
7750        // }
7751         //
7752         if(success){
7753             if(o.reset){
7754                 this.reset();
7755             }
7756             Roo.callback(o.success, o.scope, [this, action]);
7757             this.fireEvent('actioncomplete', this, action);
7758
7759         }else{
7760
7761             // failure condition..
7762             // we have a scenario where updates need confirming.
7763             // eg. if a locking scenario exists..
7764             // we look for { errors : { needs_confirm : true }} in the response.
7765             if (
7766                 (typeof(action.result) != 'undefined')  &&
7767                 (typeof(action.result.errors) != 'undefined')  &&
7768                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7769            ){
7770                 var _t = this;
7771                 Roo.log("not supported yet");
7772                  /*
7773
7774                 Roo.MessageBox.confirm(
7775                     "Change requires confirmation",
7776                     action.result.errorMsg,
7777                     function(r) {
7778                         if (r != 'yes') {
7779                             return;
7780                         }
7781                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7782                     }
7783
7784                 );
7785                 */
7786
7787
7788                 return;
7789             }
7790
7791             Roo.callback(o.failure, o.scope, [this, action]);
7792             // show an error message if no failed handler is set..
7793             if (!this.hasListener('actionfailed')) {
7794                 Roo.log("need to add dialog support");
7795                 /*
7796                 Roo.MessageBox.alert("Error",
7797                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7798                         action.result.errorMsg :
7799                         "Saving Failed, please check your entries or try again"
7800                 );
7801                 */
7802             }
7803
7804             this.fireEvent('actionfailed', this, action);
7805         }
7806
7807     },
7808     /**
7809      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7810      * @param {String} id The value to search for
7811      * @return Field
7812      */
7813     findField : function(id){
7814         var items = this.getItems();
7815         var field = items.get(id);
7816         if(!field){
7817              items.each(function(f){
7818                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7819                     field = f;
7820                     return false;
7821                 }
7822                 return true;
7823             });
7824         }
7825         return field || null;
7826     },
7827      /**
7828      * Mark fields in this form invalid in bulk.
7829      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7830      * @return {BasicForm} this
7831      */
7832     markInvalid : function(errors){
7833         if(errors instanceof Array){
7834             for(var i = 0, len = errors.length; i < len; i++){
7835                 var fieldError = errors[i];
7836                 var f = this.findField(fieldError.id);
7837                 if(f){
7838                     f.markInvalid(fieldError.msg);
7839                 }
7840             }
7841         }else{
7842             var field, id;
7843             for(id in errors){
7844                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7845                     field.markInvalid(errors[id]);
7846                 }
7847             }
7848         }
7849         //Roo.each(this.childForms || [], function (f) {
7850         //    f.markInvalid(errors);
7851         //});
7852
7853         return this;
7854     },
7855
7856     /**
7857      * Set values for fields in this form in bulk.
7858      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7859      * @return {BasicForm} this
7860      */
7861     setValues : function(values){
7862         if(values instanceof Array){ // array of objects
7863             for(var i = 0, len = values.length; i < len; i++){
7864                 var v = values[i];
7865                 var f = this.findField(v.id);
7866                 if(f){
7867                     f.setValue(v.value);
7868                     if(this.trackResetOnLoad){
7869                         f.originalValue = f.getValue();
7870                     }
7871                 }
7872             }
7873         }else{ // object hash
7874             var field, id;
7875             for(id in values){
7876                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7877
7878                     if (field.setFromData &&
7879                         field.valueField &&
7880                         field.displayField &&
7881                         // combos' with local stores can
7882                         // be queried via setValue()
7883                         // to set their value..
7884                         (field.store && !field.store.isLocal)
7885                         ) {
7886                         // it's a combo
7887                         var sd = { };
7888                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7889                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7890                         field.setFromData(sd);
7891
7892                     } else {
7893                         field.setValue(values[id]);
7894                     }
7895
7896
7897                     if(this.trackResetOnLoad){
7898                         field.originalValue = field.getValue();
7899                     }
7900                 }
7901             }
7902         }
7903
7904         //Roo.each(this.childForms || [], function (f) {
7905         //    f.setValues(values);
7906         //});
7907
7908         return this;
7909     },
7910
7911     /**
7912      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7913      * they are returned as an array.
7914      * @param {Boolean} asString
7915      * @return {Object}
7916      */
7917     getValues : function(asString){
7918         //if (this.childForms) {
7919             // copy values from the child forms
7920         //    Roo.each(this.childForms, function (f) {
7921         //        this.setValues(f.getValues());
7922         //    }, this);
7923         //}
7924
7925
7926
7927         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7928         if(asString === true){
7929             return fs;
7930         }
7931         return Roo.urlDecode(fs);
7932     },
7933
7934     /**
7935      * Returns the fields in this form as an object with key/value pairs.
7936      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7937      * @return {Object}
7938      */
7939     getFieldValues : function(with_hidden)
7940     {
7941         var items = this.getItems();
7942         var ret = {};
7943         items.each(function(f){
7944             if (!f.getName()) {
7945                 return;
7946             }
7947             var v = f.getValue();
7948             if (f.inputType =='radio') {
7949                 if (typeof(ret[f.getName()]) == 'undefined') {
7950                     ret[f.getName()] = ''; // empty..
7951                 }
7952
7953                 if (!f.el.dom.checked) {
7954                     return;
7955
7956                 }
7957                 v = f.el.dom.value;
7958
7959             }
7960
7961             // not sure if this supported any more..
7962             if ((typeof(v) == 'object') && f.getRawValue) {
7963                 v = f.getRawValue() ; // dates..
7964             }
7965             // combo boxes where name != hiddenName...
7966             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7967                 ret[f.name] = f.getRawValue();
7968             }
7969             ret[f.getName()] = v;
7970         });
7971
7972         return ret;
7973     },
7974
7975     /**
7976      * Clears all invalid messages in this form.
7977      * @return {BasicForm} this
7978      */
7979     clearInvalid : function(){
7980         var items = this.getItems();
7981
7982         items.each(function(f){
7983            f.clearInvalid();
7984         });
7985
7986
7987
7988         return this;
7989     },
7990
7991     /**
7992      * Resets this form.
7993      * @return {BasicForm} this
7994      */
7995     reset : function(){
7996         var items = this.getItems();
7997         items.each(function(f){
7998             f.reset();
7999         });
8000
8001         Roo.each(this.childForms || [], function (f) {
8002             f.reset();
8003         });
8004
8005
8006         return this;
8007     },
8008     getItems : function()
8009     {
8010         var r=new Roo.util.MixedCollection(false, function(o){
8011             return o.id || (o.id = Roo.id());
8012         });
8013         var iter = function(el) {
8014             if (el.inputEl) {
8015                 r.add(el);
8016             }
8017             if (!el.items) {
8018                 return;
8019             }
8020             Roo.each(el.items,function(e) {
8021                 iter(e);
8022             });
8023
8024
8025         };
8026
8027         iter(this);
8028         return r;
8029
8030
8031
8032
8033     }
8034
8035 });
8036
8037 Roo.apply(Roo.bootstrap.Form, {
8038     
8039     popover : {
8040         
8041         padding : 5,
8042         
8043         isApplied : false,
8044         
8045         isMasked : false,
8046         
8047         form : false,
8048         
8049         target : false,
8050         
8051         toolTip : false,
8052         
8053         intervalID : false,
8054         
8055         maskEl : false,
8056         
8057         apply : function()
8058         {
8059             if(this.isApplied){
8060                 return;
8061             }
8062             
8063             this.maskEl = {
8064                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8065                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8066                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8067                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8068             };
8069             
8070             this.maskEl.top.enableDisplayMode("block");
8071             this.maskEl.left.enableDisplayMode("block");
8072             this.maskEl.bottom.enableDisplayMode("block");
8073             this.maskEl.right.enableDisplayMode("block");
8074             
8075             this.toolTip = new Roo.bootstrap.Tooltip({
8076                 cls : 'roo-form-error-popover',
8077                 alignment : {
8078                     'left' : ['r-l', [-2,0], 'right'],
8079                     'right' : ['l-r', [2,0], 'left'],
8080                     'bottom' : ['tl-bl', [0,2], 'top'],
8081                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8082                 }
8083             });
8084             
8085             this.toolTip.render(Roo.get(document.body));
8086
8087             this.toolTip.el.enableDisplayMode("block");
8088             
8089             Roo.get(document.body).on('click', function(){
8090                 this.unmask();
8091             }, this);
8092             
8093             Roo.get(document.body).on('touchstart', function(){
8094                 this.unmask();
8095             }, this);
8096             
8097             this.isApplied = true
8098         },
8099         
8100         mask : function(form, target)
8101         {
8102             this.form = form;
8103             
8104             this.target = target;
8105             
8106             if(!this.form.errorMask || !target.el){
8107                 return;
8108             }
8109             
8110             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8111             
8112             var ot = this.target.el.calcOffsetsTo(scrollable);
8113             
8114             var scrollTo = ot[1] - this.form.maskOffset;
8115             
8116             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8117             
8118             scrollable.scrollTo('top', scrollTo);
8119             
8120             var box = this.target.el.getBox();
8121             Roo.log(box);
8122             var zIndex = Roo.bootstrap.Modal.zIndex++;
8123
8124             
8125             this.maskEl.top.setStyle('position', 'absolute');
8126             this.maskEl.top.setStyle('z-index', zIndex);
8127             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8128             this.maskEl.top.setLeft(0);
8129             this.maskEl.top.setTop(0);
8130             this.maskEl.top.show();
8131             
8132             this.maskEl.left.setStyle('position', 'absolute');
8133             this.maskEl.left.setStyle('z-index', zIndex);
8134             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8135             this.maskEl.left.setLeft(0);
8136             this.maskEl.left.setTop(box.y - this.padding);
8137             this.maskEl.left.show();
8138
8139             this.maskEl.bottom.setStyle('position', 'absolute');
8140             this.maskEl.bottom.setStyle('z-index', zIndex);
8141             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8142             this.maskEl.bottom.setLeft(0);
8143             this.maskEl.bottom.setTop(box.bottom + this.padding);
8144             this.maskEl.bottom.show();
8145
8146             this.maskEl.right.setStyle('position', 'absolute');
8147             this.maskEl.right.setStyle('z-index', zIndex);
8148             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8149             this.maskEl.right.setLeft(box.right + this.padding);
8150             this.maskEl.right.setTop(box.y - this.padding);
8151             this.maskEl.right.show();
8152
8153             this.toolTip.bindEl = this.target.el;
8154
8155             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8156
8157             var tip = this.target.blankText;
8158
8159             if(this.target.getValue() !== '' ) {
8160                 if (this.target.errorText.length) {
8161                     tip = this.target.errorText;
8162                 } else if (this.target.regexText.length){
8163                     tip = this.target.regexText;
8164                 }
8165             }
8166
8167             this.toolTip.show(tip);
8168
8169             this.intervalID = window.setInterval(function() {
8170                 Roo.bootstrap.Form.popover.unmask();
8171             }, 10000);
8172
8173             window.onwheel = function(){ return false;};
8174             
8175             (function(){ this.isMasked = true; }).defer(500, this);
8176             
8177         },
8178         
8179         unmask : function()
8180         {
8181             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8182                 return;
8183             }
8184             
8185             this.maskEl.top.setStyle('position', 'absolute');
8186             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8187             this.maskEl.top.hide();
8188
8189             this.maskEl.left.setStyle('position', 'absolute');
8190             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8191             this.maskEl.left.hide();
8192
8193             this.maskEl.bottom.setStyle('position', 'absolute');
8194             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8195             this.maskEl.bottom.hide();
8196
8197             this.maskEl.right.setStyle('position', 'absolute');
8198             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8199             this.maskEl.right.hide();
8200             
8201             this.toolTip.hide();
8202             
8203             this.toolTip.el.hide();
8204             
8205             window.onwheel = function(){ return true;};
8206             
8207             if(this.intervalID){
8208                 window.clearInterval(this.intervalID);
8209                 this.intervalID = false;
8210             }
8211             
8212             this.isMasked = false;
8213             
8214         }
8215         
8216     }
8217     
8218 });
8219
8220 /*
8221  * Based on:
8222  * Ext JS Library 1.1.1
8223  * Copyright(c) 2006-2007, Ext JS, LLC.
8224  *
8225  * Originally Released Under LGPL - original licence link has changed is not relivant.
8226  *
8227  * Fork - LGPL
8228  * <script type="text/javascript">
8229  */
8230 /**
8231  * @class Roo.form.VTypes
8232  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8233  * @singleton
8234  */
8235 Roo.form.VTypes = function(){
8236     // closure these in so they are only created once.
8237     var alpha = /^[a-zA-Z_]+$/;
8238     var alphanum = /^[a-zA-Z0-9_]+$/;
8239     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8240     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8241
8242     // All these messages and functions are configurable
8243     return {
8244         /**
8245          * The function used to validate email addresses
8246          * @param {String} value The email address
8247          */
8248         'email' : function(v){
8249             return email.test(v);
8250         },
8251         /**
8252          * The error text to display when the email validation function returns false
8253          * @type String
8254          */
8255         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8256         /**
8257          * The keystroke filter mask to be applied on email input
8258          * @type RegExp
8259          */
8260         'emailMask' : /[a-z0-9_\.\-@]/i,
8261
8262         /**
8263          * The function used to validate URLs
8264          * @param {String} value The URL
8265          */
8266         'url' : function(v){
8267             return url.test(v);
8268         },
8269         /**
8270          * The error text to display when the url validation function returns false
8271          * @type String
8272          */
8273         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8274         
8275         /**
8276          * The function used to validate alpha values
8277          * @param {String} value The value
8278          */
8279         'alpha' : function(v){
8280             return alpha.test(v);
8281         },
8282         /**
8283          * The error text to display when the alpha validation function returns false
8284          * @type String
8285          */
8286         'alphaText' : 'This field should only contain letters and _',
8287         /**
8288          * The keystroke filter mask to be applied on alpha input
8289          * @type RegExp
8290          */
8291         'alphaMask' : /[a-z_]/i,
8292
8293         /**
8294          * The function used to validate alphanumeric values
8295          * @param {String} value The value
8296          */
8297         'alphanum' : function(v){
8298             return alphanum.test(v);
8299         },
8300         /**
8301          * The error text to display when the alphanumeric validation function returns false
8302          * @type String
8303          */
8304         'alphanumText' : 'This field should only contain letters, numbers and _',
8305         /**
8306          * The keystroke filter mask to be applied on alphanumeric input
8307          * @type RegExp
8308          */
8309         'alphanumMask' : /[a-z0-9_]/i
8310     };
8311 }();/*
8312  * - LGPL
8313  *
8314  * Input
8315  * 
8316  */
8317
8318 /**
8319  * @class Roo.bootstrap.Input
8320  * @extends Roo.bootstrap.Component
8321  * Bootstrap Input class
8322  * @cfg {Boolean} disabled is it disabled
8323  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8324  * @cfg {String} name name of the input
8325  * @cfg {string} fieldLabel - the label associated
8326  * @cfg {string} placeholder - placeholder to put in text.
8327  * @cfg {string}  before - input group add on before
8328  * @cfg {string} after - input group add on after
8329  * @cfg {string} size - (lg|sm) or leave empty..
8330  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8331  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8332  * @cfg {Number} md colspan out of 12 for computer-sized screens
8333  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8334  * @cfg {string} value default value of the input
8335  * @cfg {Number} labelWidth set the width of label 
8336  * @cfg {Number} labellg set the width of label (1-12)
8337  * @cfg {Number} labelmd set the width of label (1-12)
8338  * @cfg {Number} labelsm set the width of label (1-12)
8339  * @cfg {Number} labelxs set the width of label (1-12)
8340  * @cfg {String} labelAlign (top|left)
8341  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8342  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8343  * @cfg {String} indicatorpos (left|right) default left
8344
8345  * @cfg {String} align (left|center|right) Default left
8346  * @cfg {Boolean} forceFeedback (true|false) Default false
8347  * 
8348  * 
8349  * 
8350  * 
8351  * @constructor
8352  * Create a new Input
8353  * @param {Object} config The config object
8354  */
8355
8356 Roo.bootstrap.Input = function(config){
8357     
8358     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8359     
8360     this.addEvents({
8361         /**
8362          * @event focus
8363          * Fires when this field receives input focus.
8364          * @param {Roo.form.Field} this
8365          */
8366         focus : true,
8367         /**
8368          * @event blur
8369          * Fires when this field loses input focus.
8370          * @param {Roo.form.Field} this
8371          */
8372         blur : true,
8373         /**
8374          * @event specialkey
8375          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8376          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8377          * @param {Roo.form.Field} this
8378          * @param {Roo.EventObject} e The event object
8379          */
8380         specialkey : true,
8381         /**
8382          * @event change
8383          * Fires just before the field blurs if the field value has changed.
8384          * @param {Roo.form.Field} this
8385          * @param {Mixed} newValue The new value
8386          * @param {Mixed} oldValue The original value
8387          */
8388         change : true,
8389         /**
8390          * @event invalid
8391          * Fires after the field has been marked as invalid.
8392          * @param {Roo.form.Field} this
8393          * @param {String} msg The validation message
8394          */
8395         invalid : true,
8396         /**
8397          * @event valid
8398          * Fires after the field has been validated with no errors.
8399          * @param {Roo.form.Field} this
8400          */
8401         valid : true,
8402          /**
8403          * @event keyup
8404          * Fires after the key up
8405          * @param {Roo.form.Field} this
8406          * @param {Roo.EventObject}  e The event Object
8407          */
8408         keyup : true
8409     });
8410 };
8411
8412 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8413      /**
8414      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8415       automatic validation (defaults to "keyup").
8416      */
8417     validationEvent : "keyup",
8418      /**
8419      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8420      */
8421     validateOnBlur : true,
8422     /**
8423      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8424      */
8425     validationDelay : 250,
8426      /**
8427      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8428      */
8429     focusClass : "x-form-focus",  // not needed???
8430     
8431        
8432     /**
8433      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8434      */
8435     invalidClass : "has-warning",
8436     
8437     /**
8438      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8439      */
8440     validClass : "has-success",
8441     
8442     /**
8443      * @cfg {Boolean} hasFeedback (true|false) default true
8444      */
8445     hasFeedback : true,
8446     
8447     /**
8448      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8449      */
8450     invalidFeedbackClass : "glyphicon-warning-sign",
8451     
8452     /**
8453      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8454      */
8455     validFeedbackClass : "glyphicon-ok",
8456     
8457     /**
8458      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8459      */
8460     selectOnFocus : false,
8461     
8462      /**
8463      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8464      */
8465     maskRe : null,
8466        /**
8467      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8468      */
8469     vtype : null,
8470     
8471       /**
8472      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8473      */
8474     disableKeyFilter : false,
8475     
8476        /**
8477      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8478      */
8479     disabled : false,
8480      /**
8481      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8482      */
8483     allowBlank : true,
8484     /**
8485      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8486      */
8487     blankText : "Please complete this mandatory field",
8488     
8489      /**
8490      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8491      */
8492     minLength : 0,
8493     /**
8494      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8495      */
8496     maxLength : Number.MAX_VALUE,
8497     /**
8498      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8499      */
8500     minLengthText : "The minimum length for this field is {0}",
8501     /**
8502      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8503      */
8504     maxLengthText : "The maximum length for this field is {0}",
8505   
8506     
8507     /**
8508      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8509      * If available, this function will be called only after the basic validators all return true, and will be passed the
8510      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8511      */
8512     validator : null,
8513     /**
8514      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8515      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8516      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8517      */
8518     regex : null,
8519     /**
8520      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8521      */
8522     regexText : "",
8523     /**
8524      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8525      */
8526     invalidText : "",
8527     
8528     
8529     
8530     autocomplete: false,
8531     
8532     
8533     fieldLabel : '',
8534     inputType : 'text',
8535     
8536     name : false,
8537     placeholder: false,
8538     before : false,
8539     after : false,
8540     size : false,
8541     hasFocus : false,
8542     preventMark: false,
8543     isFormField : true,
8544     value : '',
8545     labelWidth : 2,
8546     labelAlign : false,
8547     readOnly : false,
8548     align : false,
8549     formatedValue : false,
8550     forceFeedback : false,
8551     
8552     indicatorpos : 'left',
8553     
8554     labellg : 0,
8555     labelmd : 0,
8556     labelsm : 0,
8557     labelxs : 0,
8558     
8559     parentLabelAlign : function()
8560     {
8561         var parent = this;
8562         while (parent.parent()) {
8563             parent = parent.parent();
8564             if (typeof(parent.labelAlign) !='undefined') {
8565                 return parent.labelAlign;
8566             }
8567         }
8568         return 'left';
8569         
8570     },
8571     
8572     getAutoCreate : function()
8573     {
8574         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8575         
8576         var id = Roo.id();
8577         
8578         var cfg = {};
8579         
8580         if(this.inputType != 'hidden'){
8581             cfg.cls = 'form-group' //input-group
8582         }
8583         
8584         var input =  {
8585             tag: 'input',
8586             id : id,
8587             type : this.inputType,
8588             value : this.value,
8589             cls : 'form-control',
8590             placeholder : this.placeholder || '',
8591             autocomplete : this.autocomplete || 'new-password'
8592         };
8593         
8594         if(this.align){
8595             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8596         }
8597         
8598         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8599             input.maxLength = this.maxLength;
8600         }
8601         
8602         if (this.disabled) {
8603             input.disabled=true;
8604         }
8605         
8606         if (this.readOnly) {
8607             input.readonly=true;
8608         }
8609         
8610         if (this.name) {
8611             input.name = this.name;
8612         }
8613         
8614         if (this.size) {
8615             input.cls += ' input-' + this.size;
8616         }
8617         
8618         var settings=this;
8619         ['xs','sm','md','lg'].map(function(size){
8620             if (settings[size]) {
8621                 cfg.cls += ' col-' + size + '-' + settings[size];
8622             }
8623         });
8624         
8625         var inputblock = input;
8626         
8627         var feedback = {
8628             tag: 'span',
8629             cls: 'glyphicon form-control-feedback'
8630         };
8631             
8632         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8633             
8634             inputblock = {
8635                 cls : 'has-feedback',
8636                 cn :  [
8637                     input,
8638                     feedback
8639                 ] 
8640             };  
8641         }
8642         
8643         if (this.before || this.after) {
8644             
8645             inputblock = {
8646                 cls : 'input-group',
8647                 cn :  [] 
8648             };
8649             
8650             if (this.before && typeof(this.before) == 'string') {
8651                 
8652                 inputblock.cn.push({
8653                     tag :'span',
8654                     cls : 'roo-input-before input-group-addon',
8655                     html : this.before
8656                 });
8657             }
8658             if (this.before && typeof(this.before) == 'object') {
8659                 this.before = Roo.factory(this.before);
8660                 
8661                 inputblock.cn.push({
8662                     tag :'span',
8663                     cls : 'roo-input-before input-group-' +
8664                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8665                 });
8666             }
8667             
8668             inputblock.cn.push(input);
8669             
8670             if (this.after && typeof(this.after) == 'string') {
8671                 inputblock.cn.push({
8672                     tag :'span',
8673                     cls : 'roo-input-after input-group-addon',
8674                     html : this.after
8675                 });
8676             }
8677             if (this.after && typeof(this.after) == 'object') {
8678                 this.after = Roo.factory(this.after);
8679                 
8680                 inputblock.cn.push({
8681                     tag :'span',
8682                     cls : 'roo-input-after input-group-' +
8683                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8684                 });
8685             }
8686             
8687             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8688                 inputblock.cls += ' has-feedback';
8689                 inputblock.cn.push(feedback);
8690             }
8691         };
8692         
8693         if (align ==='left' && this.fieldLabel.length) {
8694             
8695             cfg.cls += ' roo-form-group-label-left';
8696             
8697             cfg.cn = [
8698                 {
8699                     tag : 'i',
8700                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8701                     tooltip : 'This field is required'
8702                 },
8703                 {
8704                     tag: 'label',
8705                     'for' :  id,
8706                     cls : 'control-label',
8707                     html : this.fieldLabel
8708
8709                 },
8710                 {
8711                     cls : "", 
8712                     cn: [
8713                         inputblock
8714                     ]
8715                 }
8716             ];
8717             
8718             var labelCfg = cfg.cn[1];
8719             var contentCfg = cfg.cn[2];
8720             
8721             if(this.indicatorpos == 'right'){
8722                 cfg.cn = [
8723                     {
8724                         tag: 'label',
8725                         'for' :  id,
8726                         cls : 'control-label',
8727                         html : this.fieldLabel
8728
8729                     },
8730                     {
8731                         tag : 'i',
8732                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8733                         tooltip : 'This field is required'
8734                     },
8735                     {
8736                         cls : "",
8737                         cn: [
8738                             inputblock
8739                         ]
8740                     }
8741
8742                 ];
8743                 
8744                 labelCfg = cfg.cn[0];
8745                 contentCfg = cfg.cn[2];
8746             
8747             }
8748             
8749             if(this.labelWidth > 12){
8750                 labelCfg.style = "width: " + this.labelWidth + 'px';
8751             }
8752             
8753             if(this.labelWidth < 13 && this.labelmd == 0){
8754                 this.labelmd = this.labelWidth;
8755             }
8756             
8757             if(this.labellg > 0){
8758                 labelCfg.cls += ' col-lg-' + this.labellg;
8759                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8760             }
8761             
8762             if(this.labelmd > 0){
8763                 labelCfg.cls += ' col-md-' + this.labelmd;
8764                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8765             }
8766             
8767             if(this.labelsm > 0){
8768                 labelCfg.cls += ' col-sm-' + this.labelsm;
8769                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8770             }
8771             
8772             if(this.labelxs > 0){
8773                 labelCfg.cls += ' col-xs-' + this.labelxs;
8774                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8775             }
8776             
8777             
8778         } else if ( this.fieldLabel.length) {
8779                 
8780             cfg.cn = [
8781                 {
8782                     tag : 'i',
8783                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8784                     tooltip : 'This field is required'
8785                 },
8786                 {
8787                     tag: 'label',
8788                    //cls : 'input-group-addon',
8789                     html : this.fieldLabel
8790
8791                 },
8792
8793                inputblock
8794
8795            ];
8796            
8797            if(this.indicatorpos == 'right'){
8798                 
8799                 cfg.cn = [
8800                     {
8801                         tag: 'label',
8802                        //cls : 'input-group-addon',
8803                         html : this.fieldLabel
8804
8805                     },
8806                     {
8807                         tag : 'i',
8808                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8809                         tooltip : 'This field is required'
8810                     },
8811
8812                    inputblock
8813
8814                ];
8815
8816             }
8817
8818         } else {
8819             
8820             cfg.cn = [
8821
8822                     inputblock
8823
8824             ];
8825                 
8826                 
8827         };
8828         
8829         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8830            cfg.cls += ' navbar-form';
8831         }
8832         
8833         if (this.parentType === 'NavGroup') {
8834            cfg.cls += ' navbar-form';
8835            cfg.tag = 'li';
8836         }
8837         
8838         return cfg;
8839         
8840     },
8841     /**
8842      * return the real input element.
8843      */
8844     inputEl: function ()
8845     {
8846         return this.el.select('input.form-control',true).first();
8847     },
8848     
8849     tooltipEl : function()
8850     {
8851         return this.inputEl();
8852     },
8853     
8854     indicatorEl : function()
8855     {
8856         var indicator = this.el.select('i.roo-required-indicator',true).first();
8857         
8858         if(!indicator){
8859             return false;
8860         }
8861         
8862         return indicator;
8863         
8864     },
8865     
8866     setDisabled : function(v)
8867     {
8868         var i  = this.inputEl().dom;
8869         if (!v) {
8870             i.removeAttribute('disabled');
8871             return;
8872             
8873         }
8874         i.setAttribute('disabled','true');
8875     },
8876     initEvents : function()
8877     {
8878           
8879         this.inputEl().on("keydown" , this.fireKey,  this);
8880         this.inputEl().on("focus", this.onFocus,  this);
8881         this.inputEl().on("blur", this.onBlur,  this);
8882         
8883         this.inputEl().relayEvent('keyup', this);
8884         
8885         this.indicator = this.indicatorEl();
8886         
8887         if(this.indicator){
8888             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8889             this.indicator.hide();
8890         }
8891  
8892         // reference to original value for reset
8893         this.originalValue = this.getValue();
8894         //Roo.form.TextField.superclass.initEvents.call(this);
8895         if(this.validationEvent == 'keyup'){
8896             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8897             this.inputEl().on('keyup', this.filterValidation, this);
8898         }
8899         else if(this.validationEvent !== false){
8900             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8901         }
8902         
8903         if(this.selectOnFocus){
8904             this.on("focus", this.preFocus, this);
8905             
8906         }
8907         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8908             this.inputEl().on("keypress", this.filterKeys, this);
8909         } else {
8910             this.inputEl().relayEvent('keypress', this);
8911         }
8912        /* if(this.grow){
8913             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8914             this.el.on("click", this.autoSize,  this);
8915         }
8916         */
8917         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8918             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8919         }
8920         
8921         if (typeof(this.before) == 'object') {
8922             this.before.render(this.el.select('.roo-input-before',true).first());
8923         }
8924         if (typeof(this.after) == 'object') {
8925             this.after.render(this.el.select('.roo-input-after',true).first());
8926         }
8927         
8928         
8929     },
8930     filterValidation : function(e){
8931         if(!e.isNavKeyPress()){
8932             this.validationTask.delay(this.validationDelay);
8933         }
8934     },
8935      /**
8936      * Validates the field value
8937      * @return {Boolean} True if the value is valid, else false
8938      */
8939     validate : function(){
8940         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8941         if(this.disabled || this.validateValue(this.getRawValue())){
8942             this.markValid();
8943             return true;
8944         }
8945         
8946         this.markInvalid();
8947         return false;
8948     },
8949     
8950     
8951     /**
8952      * Validates a value according to the field's validation rules and marks the field as invalid
8953      * if the validation fails
8954      * @param {Mixed} value The value to validate
8955      * @return {Boolean} True if the value is valid, else false
8956      */
8957     validateValue : function(value){
8958         if(value.length < 1)  { // if it's blank
8959             if(this.allowBlank){
8960                 return true;
8961             }            
8962             return this.inputEl().hasClass('hide') ? true : false;
8963         }
8964         
8965         if(value.length < this.minLength){
8966             return false;
8967         }
8968         if(value.length > this.maxLength){
8969             return false;
8970         }
8971         if(this.vtype){
8972             var vt = Roo.form.VTypes;
8973             if(!vt[this.vtype](value, this)){
8974                 return false;
8975             }
8976         }
8977         if(typeof this.validator == "function"){
8978             var msg = this.validator(value);
8979             if(msg !== true){
8980                 return false;
8981             }
8982         }
8983         
8984         if(this.regex && !this.regex.test(value)){
8985             return false;
8986         }
8987         
8988         return true;
8989     },
8990
8991     
8992     
8993      // private
8994     fireKey : function(e){
8995         //Roo.log('field ' + e.getKey());
8996         if(e.isNavKeyPress()){
8997             this.fireEvent("specialkey", this, e);
8998         }
8999     },
9000     focus : function (selectText){
9001         if(this.rendered){
9002             this.inputEl().focus();
9003             if(selectText === true){
9004                 this.inputEl().dom.select();
9005             }
9006         }
9007         return this;
9008     } ,
9009     
9010     onFocus : function(){
9011         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9012            // this.el.addClass(this.focusClass);
9013         }
9014         if(!this.hasFocus){
9015             this.hasFocus = true;
9016             this.startValue = this.getValue();
9017             this.fireEvent("focus", this);
9018         }
9019     },
9020     
9021     beforeBlur : Roo.emptyFn,
9022
9023     
9024     // private
9025     onBlur : function(){
9026         this.beforeBlur();
9027         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9028             //this.el.removeClass(this.focusClass);
9029         }
9030         this.hasFocus = false;
9031         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9032             this.validate();
9033         }
9034         var v = this.getValue();
9035         if(String(v) !== String(this.startValue)){
9036             this.fireEvent('change', this, v, this.startValue);
9037         }
9038         this.fireEvent("blur", this);
9039     },
9040     
9041     /**
9042      * Resets the current field value to the originally loaded value and clears any validation messages
9043      */
9044     reset : function(){
9045         this.setValue(this.originalValue);
9046         this.validate();
9047     },
9048      /**
9049      * Returns the name of the field
9050      * @return {Mixed} name The name field
9051      */
9052     getName: function(){
9053         return this.name;
9054     },
9055      /**
9056      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9057      * @return {Mixed} value The field value
9058      */
9059     getValue : function(){
9060         
9061         var v = this.inputEl().getValue();
9062         
9063         return v;
9064     },
9065     /**
9066      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9067      * @return {Mixed} value The field value
9068      */
9069     getRawValue : function(){
9070         var v = this.inputEl().getValue();
9071         
9072         return v;
9073     },
9074     
9075     /**
9076      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9077      * @param {Mixed} value The value to set
9078      */
9079     setRawValue : function(v){
9080         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9081     },
9082     
9083     selectText : function(start, end){
9084         var v = this.getRawValue();
9085         if(v.length > 0){
9086             start = start === undefined ? 0 : start;
9087             end = end === undefined ? v.length : end;
9088             var d = this.inputEl().dom;
9089             if(d.setSelectionRange){
9090                 d.setSelectionRange(start, end);
9091             }else if(d.createTextRange){
9092                 var range = d.createTextRange();
9093                 range.moveStart("character", start);
9094                 range.moveEnd("character", v.length-end);
9095                 range.select();
9096             }
9097         }
9098     },
9099     
9100     /**
9101      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9102      * @param {Mixed} value The value to set
9103      */
9104     setValue : function(v){
9105         this.value = v;
9106         if(this.rendered){
9107             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9108             this.validate();
9109         }
9110     },
9111     
9112     /*
9113     processValue : function(value){
9114         if(this.stripCharsRe){
9115             var newValue = value.replace(this.stripCharsRe, '');
9116             if(newValue !== value){
9117                 this.setRawValue(newValue);
9118                 return newValue;
9119             }
9120         }
9121         return value;
9122     },
9123   */
9124     preFocus : function(){
9125         
9126         if(this.selectOnFocus){
9127             this.inputEl().dom.select();
9128         }
9129     },
9130     filterKeys : function(e){
9131         var k = e.getKey();
9132         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9133             return;
9134         }
9135         var c = e.getCharCode(), cc = String.fromCharCode(c);
9136         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9137             return;
9138         }
9139         if(!this.maskRe.test(cc)){
9140             e.stopEvent();
9141         }
9142     },
9143      /**
9144      * Clear any invalid styles/messages for this field
9145      */
9146     clearInvalid : function(){
9147         
9148         if(!this.el || this.preventMark){ // not rendered
9149             return;
9150         }
9151         
9152      
9153         this.el.removeClass(this.invalidClass);
9154         
9155         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9156             
9157             var feedback = this.el.select('.form-control-feedback', true).first();
9158             
9159             if(feedback){
9160                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9161             }
9162             
9163         }
9164         
9165         this.fireEvent('valid', this);
9166     },
9167     
9168      /**
9169      * Mark this field as valid
9170      */
9171     markValid : function()
9172     {
9173         if(!this.el  || this.preventMark){ // not rendered...
9174             return;
9175         }
9176         
9177         this.el.removeClass([this.invalidClass, this.validClass]);
9178         
9179         var feedback = this.el.select('.form-control-feedback', true).first();
9180             
9181         if(feedback){
9182             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9183         }
9184
9185         if(this.disabled){
9186             return;
9187         }
9188         
9189         if(this.allowBlank && !this.getRawValue().length){
9190             return;
9191         }
9192         
9193         if(this.indicator){
9194             this.indicator.hide();
9195         }
9196         
9197         this.el.addClass(this.validClass);
9198         
9199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9200             
9201             var feedback = this.el.select('.form-control-feedback', true).first();
9202             
9203             if(feedback){
9204                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9205                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9206             }
9207             
9208         }
9209         
9210         this.fireEvent('valid', this);
9211     },
9212     
9213      /**
9214      * Mark this field as invalid
9215      * @param {String} msg The validation message
9216      */
9217     markInvalid : function(msg)
9218     {
9219         if(!this.el  || this.preventMark){ // not rendered
9220             return;
9221         }
9222         
9223         this.el.removeClass([this.invalidClass, this.validClass]);
9224         
9225         var feedback = this.el.select('.form-control-feedback', true).first();
9226             
9227         if(feedback){
9228             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9229         }
9230
9231         if(this.disabled){
9232             return;
9233         }
9234         
9235         if(this.allowBlank && !this.getRawValue().length){
9236             return;
9237         }
9238         
9239         if(this.indicator){
9240             this.indicator.show();
9241         }
9242         
9243         this.el.addClass(this.invalidClass);
9244         
9245         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9246             
9247             var feedback = this.el.select('.form-control-feedback', true).first();
9248             
9249             if(feedback){
9250                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9251                 
9252                 if(this.getValue().length || this.forceFeedback){
9253                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9254                 }
9255                 
9256             }
9257             
9258         }
9259         
9260         this.fireEvent('invalid', this, msg);
9261     },
9262     // private
9263     SafariOnKeyDown : function(event)
9264     {
9265         // this is a workaround for a password hang bug on chrome/ webkit.
9266         if (this.inputEl().dom.type != 'password') {
9267             return;
9268         }
9269         
9270         var isSelectAll = false;
9271         
9272         if(this.inputEl().dom.selectionEnd > 0){
9273             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9274         }
9275         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9276             event.preventDefault();
9277             this.setValue('');
9278             return;
9279         }
9280         
9281         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9282             
9283             event.preventDefault();
9284             // this is very hacky as keydown always get's upper case.
9285             //
9286             var cc = String.fromCharCode(event.getCharCode());
9287             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9288             
9289         }
9290     },
9291     adjustWidth : function(tag, w){
9292         tag = tag.toLowerCase();
9293         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9294             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9295                 if(tag == 'input'){
9296                     return w + 2;
9297                 }
9298                 if(tag == 'textarea'){
9299                     return w-2;
9300                 }
9301             }else if(Roo.isOpera){
9302                 if(tag == 'input'){
9303                     return w + 2;
9304                 }
9305                 if(tag == 'textarea'){
9306                     return w-2;
9307                 }
9308             }
9309         }
9310         return w;
9311     }
9312     
9313 });
9314
9315  
9316 /*
9317  * - LGPL
9318  *
9319  * Input
9320  * 
9321  */
9322
9323 /**
9324  * @class Roo.bootstrap.TextArea
9325  * @extends Roo.bootstrap.Input
9326  * Bootstrap TextArea class
9327  * @cfg {Number} cols Specifies the visible width of a text area
9328  * @cfg {Number} rows Specifies the visible number of lines in a text area
9329  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9330  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9331  * @cfg {string} html text
9332  * 
9333  * @constructor
9334  * Create a new TextArea
9335  * @param {Object} config The config object
9336  */
9337
9338 Roo.bootstrap.TextArea = function(config){
9339     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9340    
9341 };
9342
9343 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9344      
9345     cols : false,
9346     rows : 5,
9347     readOnly : false,
9348     warp : 'soft',
9349     resize : false,
9350     value: false,
9351     html: false,
9352     
9353     getAutoCreate : function(){
9354         
9355         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9356         
9357         var id = Roo.id();
9358         
9359         var cfg = {};
9360         
9361         var input =  {
9362             tag: 'textarea',
9363             id : id,
9364             warp : this.warp,
9365             rows : this.rows,
9366             value : this.value || '',
9367             html: this.html || '',
9368             cls : 'form-control',
9369             placeholder : this.placeholder || '' 
9370             
9371         };
9372         
9373         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9374             input.maxLength = this.maxLength;
9375         }
9376         
9377         if(this.resize){
9378             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9379         }
9380         
9381         if(this.cols){
9382             input.cols = this.cols;
9383         }
9384         
9385         if (this.readOnly) {
9386             input.readonly = true;
9387         }
9388         
9389         if (this.name) {
9390             input.name = this.name;
9391         }
9392         
9393         if (this.size) {
9394             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9395         }
9396         
9397         var settings=this;
9398         ['xs','sm','md','lg'].map(function(size){
9399             if (settings[size]) {
9400                 cfg.cls += ' col-' + size + '-' + settings[size];
9401             }
9402         });
9403         
9404         var inputblock = input;
9405         
9406         if(this.hasFeedback && !this.allowBlank){
9407             
9408             var feedback = {
9409                 tag: 'span',
9410                 cls: 'glyphicon form-control-feedback'
9411             };
9412
9413             inputblock = {
9414                 cls : 'has-feedback',
9415                 cn :  [
9416                     input,
9417                     feedback
9418                 ] 
9419             };  
9420         }
9421         
9422         
9423         if (this.before || this.after) {
9424             
9425             inputblock = {
9426                 cls : 'input-group',
9427                 cn :  [] 
9428             };
9429             if (this.before) {
9430                 inputblock.cn.push({
9431                     tag :'span',
9432                     cls : 'input-group-addon',
9433                     html : this.before
9434                 });
9435             }
9436             
9437             inputblock.cn.push(input);
9438             
9439             if(this.hasFeedback && !this.allowBlank){
9440                 inputblock.cls += ' has-feedback';
9441                 inputblock.cn.push(feedback);
9442             }
9443             
9444             if (this.after) {
9445                 inputblock.cn.push({
9446                     tag :'span',
9447                     cls : 'input-group-addon',
9448                     html : this.after
9449                 });
9450             }
9451             
9452         }
9453         
9454         if (align ==='left' && this.fieldLabel.length) {
9455             cfg.cn = [
9456                 {
9457                     tag: 'label',
9458                     'for' :  id,
9459                     cls : 'control-label',
9460                     html : this.fieldLabel
9461                 },
9462                 {
9463                     cls : "",
9464                     cn: [
9465                         inputblock
9466                     ]
9467                 }
9468
9469             ];
9470             
9471             if(this.labelWidth > 12){
9472                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9473             }
9474
9475             if(this.labelWidth < 13 && this.labelmd == 0){
9476                 this.labelmd = this.labelWidth;
9477             }
9478
9479             if(this.labellg > 0){
9480                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9481                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9482             }
9483
9484             if(this.labelmd > 0){
9485                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9486                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9487             }
9488
9489             if(this.labelsm > 0){
9490                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9491                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9492             }
9493
9494             if(this.labelxs > 0){
9495                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9496                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9497             }
9498             
9499         } else if ( this.fieldLabel.length) {
9500             cfg.cn = [
9501
9502                {
9503                    tag: 'label',
9504                    //cls : 'input-group-addon',
9505                    html : this.fieldLabel
9506
9507                },
9508
9509                inputblock
9510
9511            ];
9512
9513         } else {
9514
9515             cfg.cn = [
9516
9517                 inputblock
9518
9519             ];
9520                 
9521         }
9522         
9523         if (this.disabled) {
9524             input.disabled=true;
9525         }
9526         
9527         return cfg;
9528         
9529     },
9530     /**
9531      * return the real textarea element.
9532      */
9533     inputEl: function ()
9534     {
9535         return this.el.select('textarea.form-control',true).first();
9536     },
9537     
9538     /**
9539      * Clear any invalid styles/messages for this field
9540      */
9541     clearInvalid : function()
9542     {
9543         
9544         if(!this.el || this.preventMark){ // not rendered
9545             return;
9546         }
9547         
9548         var label = this.el.select('label', true).first();
9549         var icon = this.el.select('i.fa-star', true).first();
9550         
9551         if(label && icon){
9552             icon.remove();
9553         }
9554         
9555         this.el.removeClass(this.invalidClass);
9556         
9557         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9558             
9559             var feedback = this.el.select('.form-control-feedback', true).first();
9560             
9561             if(feedback){
9562                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9563             }
9564             
9565         }
9566         
9567         this.fireEvent('valid', this);
9568     },
9569     
9570      /**
9571      * Mark this field as valid
9572      */
9573     markValid : function()
9574     {
9575         if(!this.el  || this.preventMark){ // not rendered
9576             return;
9577         }
9578         
9579         this.el.removeClass([this.invalidClass, this.validClass]);
9580         
9581         var feedback = this.el.select('.form-control-feedback', true).first();
9582             
9583         if(feedback){
9584             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9585         }
9586
9587         if(this.disabled || this.allowBlank){
9588             return;
9589         }
9590         
9591         var label = this.el.select('label', true).first();
9592         var icon = this.el.select('i.fa-star', true).first();
9593         
9594         if(label && icon){
9595             icon.remove();
9596         }
9597         
9598         this.el.addClass(this.validClass);
9599         
9600         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9601             
9602             var feedback = this.el.select('.form-control-feedback', true).first();
9603             
9604             if(feedback){
9605                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9606                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9607             }
9608             
9609         }
9610         
9611         this.fireEvent('valid', this);
9612     },
9613     
9614      /**
9615      * Mark this field as invalid
9616      * @param {String} msg The validation message
9617      */
9618     markInvalid : function(msg)
9619     {
9620         if(!this.el  || this.preventMark){ // not rendered
9621             return;
9622         }
9623         
9624         this.el.removeClass([this.invalidClass, this.validClass]);
9625         
9626         var feedback = this.el.select('.form-control-feedback', true).first();
9627             
9628         if(feedback){
9629             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9630         }
9631
9632         if(this.disabled || this.allowBlank){
9633             return;
9634         }
9635         
9636         var label = this.el.select('label', true).first();
9637         var icon = this.el.select('i.fa-star', true).first();
9638         
9639         if(!this.getValue().length && label && !icon){
9640             this.el.createChild({
9641                 tag : 'i',
9642                 cls : 'text-danger fa fa-lg fa-star',
9643                 tooltip : 'This field is required',
9644                 style : 'margin-right:5px;'
9645             }, label, true);
9646         }
9647
9648         this.el.addClass(this.invalidClass);
9649         
9650         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9651             
9652             var feedback = this.el.select('.form-control-feedback', true).first();
9653             
9654             if(feedback){
9655                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9656                 
9657                 if(this.getValue().length || this.forceFeedback){
9658                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9659                 }
9660                 
9661             }
9662             
9663         }
9664         
9665         this.fireEvent('invalid', this, msg);
9666     }
9667 });
9668
9669  
9670 /*
9671  * - LGPL
9672  *
9673  * trigger field - base class for combo..
9674  * 
9675  */
9676  
9677 /**
9678  * @class Roo.bootstrap.TriggerField
9679  * @extends Roo.bootstrap.Input
9680  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9681  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9682  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9683  * for which you can provide a custom implementation.  For example:
9684  * <pre><code>
9685 var trigger = new Roo.bootstrap.TriggerField();
9686 trigger.onTriggerClick = myTriggerFn;
9687 trigger.applyTo('my-field');
9688 </code></pre>
9689  *
9690  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9691  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9692  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9693  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9694  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9695
9696  * @constructor
9697  * Create a new TriggerField.
9698  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9699  * to the base TextField)
9700  */
9701 Roo.bootstrap.TriggerField = function(config){
9702     this.mimicing = false;
9703     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9704 };
9705
9706 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9707     /**
9708      * @cfg {String} triggerClass A CSS class to apply to the trigger
9709      */
9710      /**
9711      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9712      */
9713     hideTrigger:false,
9714
9715     /**
9716      * @cfg {Boolean} removable (true|false) special filter default false
9717      */
9718     removable : false,
9719     
9720     /** @cfg {Boolean} grow @hide */
9721     /** @cfg {Number} growMin @hide */
9722     /** @cfg {Number} growMax @hide */
9723
9724     /**
9725      * @hide 
9726      * @method
9727      */
9728     autoSize: Roo.emptyFn,
9729     // private
9730     monitorTab : true,
9731     // private
9732     deferHeight : true,
9733
9734     
9735     actionMode : 'wrap',
9736     
9737     caret : false,
9738     
9739     
9740     getAutoCreate : function(){
9741        
9742         var align = this.labelAlign || this.parentLabelAlign();
9743         
9744         var id = Roo.id();
9745         
9746         var cfg = {
9747             cls: 'form-group' //input-group
9748         };
9749         
9750         
9751         var input =  {
9752             tag: 'input',
9753             id : id,
9754             type : this.inputType,
9755             cls : 'form-control',
9756             autocomplete: 'new-password',
9757             placeholder : this.placeholder || '' 
9758             
9759         };
9760         if (this.name) {
9761             input.name = this.name;
9762         }
9763         if (this.size) {
9764             input.cls += ' input-' + this.size;
9765         }
9766         
9767         if (this.disabled) {
9768             input.disabled=true;
9769         }
9770         
9771         var inputblock = input;
9772         
9773         if(this.hasFeedback && !this.allowBlank){
9774             
9775             var feedback = {
9776                 tag: 'span',
9777                 cls: 'glyphicon form-control-feedback'
9778             };
9779             
9780             if(this.removable && !this.editable && !this.tickable){
9781                 inputblock = {
9782                     cls : 'has-feedback',
9783                     cn :  [
9784                         inputblock,
9785                         {
9786                             tag: 'button',
9787                             html : 'x',
9788                             cls : 'roo-combo-removable-btn close'
9789                         },
9790                         feedback
9791                     ] 
9792                 };
9793             } else {
9794                 inputblock = {
9795                     cls : 'has-feedback',
9796                     cn :  [
9797                         inputblock,
9798                         feedback
9799                     ] 
9800                 };
9801             }
9802
9803         } else {
9804             if(this.removable && !this.editable && !this.tickable){
9805                 inputblock = {
9806                     cls : 'roo-removable',
9807                     cn :  [
9808                         inputblock,
9809                         {
9810                             tag: 'button',
9811                             html : 'x',
9812                             cls : 'roo-combo-removable-btn close'
9813                         }
9814                     ] 
9815                 };
9816             }
9817         }
9818         
9819         if (this.before || this.after) {
9820             
9821             inputblock = {
9822                 cls : 'input-group',
9823                 cn :  [] 
9824             };
9825             if (this.before) {
9826                 inputblock.cn.push({
9827                     tag :'span',
9828                     cls : 'input-group-addon',
9829                     html : this.before
9830                 });
9831             }
9832             
9833             inputblock.cn.push(input);
9834             
9835             if(this.hasFeedback && !this.allowBlank){
9836                 inputblock.cls += ' has-feedback';
9837                 inputblock.cn.push(feedback);
9838             }
9839             
9840             if (this.after) {
9841                 inputblock.cn.push({
9842                     tag :'span',
9843                     cls : 'input-group-addon',
9844                     html : this.after
9845                 });
9846             }
9847             
9848         };
9849         
9850         var box = {
9851             tag: 'div',
9852             cn: [
9853                 {
9854                     tag: 'input',
9855                     type : 'hidden',
9856                     cls: 'form-hidden-field'
9857                 },
9858                 inputblock
9859             ]
9860             
9861         };
9862         
9863         if(this.multiple){
9864             box = {
9865                 tag: 'div',
9866                 cn: [
9867                     {
9868                         tag: 'input',
9869                         type : 'hidden',
9870                         cls: 'form-hidden-field'
9871                     },
9872                     {
9873                         tag: 'ul',
9874                         cls: 'roo-select2-choices',
9875                         cn:[
9876                             {
9877                                 tag: 'li',
9878                                 cls: 'roo-select2-search-field',
9879                                 cn: [
9880
9881                                     inputblock
9882                                 ]
9883                             }
9884                         ]
9885                     }
9886                 ]
9887             }
9888         };
9889         
9890         var combobox = {
9891             cls: 'roo-select2-container input-group',
9892             cn: [
9893                 box
9894 //                {
9895 //                    tag: 'ul',
9896 //                    cls: 'typeahead typeahead-long dropdown-menu',
9897 //                    style: 'display:none'
9898 //                }
9899             ]
9900         };
9901         
9902         if(!this.multiple && this.showToggleBtn){
9903             
9904             var caret = {
9905                         tag: 'span',
9906                         cls: 'caret'
9907              };
9908             if (this.caret != false) {
9909                 caret = {
9910                      tag: 'i',
9911                      cls: 'fa fa-' + this.caret
9912                 };
9913                 
9914             }
9915             
9916             combobox.cn.push({
9917                 tag :'span',
9918                 cls : 'input-group-addon btn dropdown-toggle',
9919                 cn : [
9920                     caret,
9921                     {
9922                         tag: 'span',
9923                         cls: 'combobox-clear',
9924                         cn  : [
9925                             {
9926                                 tag : 'i',
9927                                 cls: 'icon-remove'
9928                             }
9929                         ]
9930                     }
9931                 ]
9932
9933             })
9934         }
9935         
9936         if(this.multiple){
9937             combobox.cls += ' roo-select2-container-multi';
9938         }
9939         
9940         if (align ==='left' && this.fieldLabel.length) {
9941             
9942             cfg.cls += ' roo-form-group-label-left';
9943
9944             cfg.cn = [
9945                 {
9946                     tag : 'i',
9947                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9948                     tooltip : 'This field is required'
9949                 },
9950                 {
9951                     tag: 'label',
9952                     'for' :  id,
9953                     cls : 'control-label',
9954                     html : this.fieldLabel
9955
9956                 },
9957                 {
9958                     cls : "", 
9959                     cn: [
9960                         combobox
9961                     ]
9962                 }
9963
9964             ];
9965             
9966             var labelCfg = cfg.cn[1];
9967             var contentCfg = cfg.cn[2];
9968             
9969             if(this.indicatorpos == 'right'){
9970                 cfg.cn = [
9971                     {
9972                         tag: 'label',
9973                         'for' :  id,
9974                         cls : 'control-label',
9975                         cn : [
9976                             {
9977                                 tag : 'span',
9978                                 html : this.fieldLabel
9979                             },
9980                             {
9981                                 tag : 'i',
9982                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9983                                 tooltip : 'This field is required'
9984                             }
9985                         ]
9986                     },
9987                     {
9988                         cls : "", 
9989                         cn: [
9990                             combobox
9991                         ]
9992                     }
9993
9994                 ];
9995                 
9996                 labelCfg = cfg.cn[0];
9997                 contentCfg = cfg.cn[1];
9998             }
9999             
10000             if(this.labelWidth > 12){
10001                 labelCfg.style = "width: " + this.labelWidth + 'px';
10002             }
10003             
10004             if(this.labelWidth < 13 && this.labelmd == 0){
10005                 this.labelmd = this.labelWidth;
10006             }
10007             
10008             if(this.labellg > 0){
10009                 labelCfg.cls += ' col-lg-' + this.labellg;
10010                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10011             }
10012             
10013             if(this.labelmd > 0){
10014                 labelCfg.cls += ' col-md-' + this.labelmd;
10015                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10016             }
10017             
10018             if(this.labelsm > 0){
10019                 labelCfg.cls += ' col-sm-' + this.labelsm;
10020                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10021             }
10022             
10023             if(this.labelxs > 0){
10024                 labelCfg.cls += ' col-xs-' + this.labelxs;
10025                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10026             }
10027             
10028         } else if ( this.fieldLabel.length) {
10029 //                Roo.log(" label");
10030             cfg.cn = [
10031                 {
10032                    tag : 'i',
10033                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10034                    tooltip : 'This field is required'
10035                },
10036                {
10037                    tag: 'label',
10038                    //cls : 'input-group-addon',
10039                    html : this.fieldLabel
10040
10041                },
10042
10043                combobox
10044
10045             ];
10046             
10047             if(this.indicatorpos == 'right'){
10048                 
10049                 cfg.cn = [
10050                     {
10051                        tag: 'label',
10052                        cn : [
10053                            {
10054                                tag : 'span',
10055                                html : this.fieldLabel
10056                            },
10057                            {
10058                               tag : 'i',
10059                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10060                               tooltip : 'This field is required'
10061                            }
10062                        ]
10063
10064                     },
10065                     combobox
10066
10067                 ];
10068
10069             }
10070
10071         } else {
10072             
10073 //                Roo.log(" no label && no align");
10074                 cfg = combobox
10075                      
10076                 
10077         }
10078         
10079         var settings=this;
10080         ['xs','sm','md','lg'].map(function(size){
10081             if (settings[size]) {
10082                 cfg.cls += ' col-' + size + '-' + settings[size];
10083             }
10084         });
10085         
10086         return cfg;
10087         
10088     },
10089     
10090     
10091     
10092     // private
10093     onResize : function(w, h){
10094 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10095 //        if(typeof w == 'number'){
10096 //            var x = w - this.trigger.getWidth();
10097 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10098 //            this.trigger.setStyle('left', x+'px');
10099 //        }
10100     },
10101
10102     // private
10103     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10104
10105     // private
10106     getResizeEl : function(){
10107         return this.inputEl();
10108     },
10109
10110     // private
10111     getPositionEl : function(){
10112         return this.inputEl();
10113     },
10114
10115     // private
10116     alignErrorIcon : function(){
10117         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10118     },
10119
10120     // private
10121     initEvents : function(){
10122         
10123         this.createList();
10124         
10125         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10126         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10127         if(!this.multiple && this.showToggleBtn){
10128             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10129             if(this.hideTrigger){
10130                 this.trigger.setDisplayed(false);
10131             }
10132             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10133         }
10134         
10135         if(this.multiple){
10136             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10137         }
10138         
10139         if(this.removable && !this.editable && !this.tickable){
10140             var close = this.closeTriggerEl();
10141             
10142             if(close){
10143                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10144                 close.on('click', this.removeBtnClick, this, close);
10145             }
10146         }
10147         
10148         //this.trigger.addClassOnOver('x-form-trigger-over');
10149         //this.trigger.addClassOnClick('x-form-trigger-click');
10150         
10151         //if(!this.width){
10152         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10153         //}
10154     },
10155     
10156     closeTriggerEl : function()
10157     {
10158         var close = this.el.select('.roo-combo-removable-btn', true).first();
10159         return close ? close : false;
10160     },
10161     
10162     removeBtnClick : function(e, h, el)
10163     {
10164         e.preventDefault();
10165         
10166         if(this.fireEvent("remove", this) !== false){
10167             this.reset();
10168             this.fireEvent("afterremove", this)
10169         }
10170     },
10171     
10172     createList : function()
10173     {
10174         this.list = Roo.get(document.body).createChild({
10175             tag: 'ul',
10176             cls: 'typeahead typeahead-long dropdown-menu',
10177             style: 'display:none'
10178         });
10179         
10180         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10181         
10182     },
10183
10184     // private
10185     initTrigger : function(){
10186        
10187     },
10188
10189     // private
10190     onDestroy : function(){
10191         if(this.trigger){
10192             this.trigger.removeAllListeners();
10193           //  this.trigger.remove();
10194         }
10195         //if(this.wrap){
10196         //    this.wrap.remove();
10197         //}
10198         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10199     },
10200
10201     // private
10202     onFocus : function(){
10203         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10204         /*
10205         if(!this.mimicing){
10206             this.wrap.addClass('x-trigger-wrap-focus');
10207             this.mimicing = true;
10208             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10209             if(this.monitorTab){
10210                 this.el.on("keydown", this.checkTab, this);
10211             }
10212         }
10213         */
10214     },
10215
10216     // private
10217     checkTab : function(e){
10218         if(e.getKey() == e.TAB){
10219             this.triggerBlur();
10220         }
10221     },
10222
10223     // private
10224     onBlur : function(){
10225         // do nothing
10226     },
10227
10228     // private
10229     mimicBlur : function(e, t){
10230         /*
10231         if(!this.wrap.contains(t) && this.validateBlur()){
10232             this.triggerBlur();
10233         }
10234         */
10235     },
10236
10237     // private
10238     triggerBlur : function(){
10239         this.mimicing = false;
10240         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10241         if(this.monitorTab){
10242             this.el.un("keydown", this.checkTab, this);
10243         }
10244         //this.wrap.removeClass('x-trigger-wrap-focus');
10245         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10246     },
10247
10248     // private
10249     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10250     validateBlur : function(e, t){
10251         return true;
10252     },
10253
10254     // private
10255     onDisable : function(){
10256         this.inputEl().dom.disabled = true;
10257         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10258         //if(this.wrap){
10259         //    this.wrap.addClass('x-item-disabled');
10260         //}
10261     },
10262
10263     // private
10264     onEnable : function(){
10265         this.inputEl().dom.disabled = false;
10266         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10267         //if(this.wrap){
10268         //    this.el.removeClass('x-item-disabled');
10269         //}
10270     },
10271
10272     // private
10273     onShow : function(){
10274         var ae = this.getActionEl();
10275         
10276         if(ae){
10277             ae.dom.style.display = '';
10278             ae.dom.style.visibility = 'visible';
10279         }
10280     },
10281
10282     // private
10283     
10284     onHide : function(){
10285         var ae = this.getActionEl();
10286         ae.dom.style.display = 'none';
10287     },
10288
10289     /**
10290      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10291      * by an implementing function.
10292      * @method
10293      * @param {EventObject} e
10294      */
10295     onTriggerClick : Roo.emptyFn
10296 });
10297  /*
10298  * Based on:
10299  * Ext JS Library 1.1.1
10300  * Copyright(c) 2006-2007, Ext JS, LLC.
10301  *
10302  * Originally Released Under LGPL - original licence link has changed is not relivant.
10303  *
10304  * Fork - LGPL
10305  * <script type="text/javascript">
10306  */
10307
10308
10309 /**
10310  * @class Roo.data.SortTypes
10311  * @singleton
10312  * Defines the default sorting (casting?) comparison functions used when sorting data.
10313  */
10314 Roo.data.SortTypes = {
10315     /**
10316      * Default sort that does nothing
10317      * @param {Mixed} s The value being converted
10318      * @return {Mixed} The comparison value
10319      */
10320     none : function(s){
10321         return s;
10322     },
10323     
10324     /**
10325      * The regular expression used to strip tags
10326      * @type {RegExp}
10327      * @property
10328      */
10329     stripTagsRE : /<\/?[^>]+>/gi,
10330     
10331     /**
10332      * Strips all HTML tags to sort on text only
10333      * @param {Mixed} s The value being converted
10334      * @return {String} The comparison value
10335      */
10336     asText : function(s){
10337         return String(s).replace(this.stripTagsRE, "");
10338     },
10339     
10340     /**
10341      * Strips all HTML tags to sort on text only - Case insensitive
10342      * @param {Mixed} s The value being converted
10343      * @return {String} The comparison value
10344      */
10345     asUCText : function(s){
10346         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10347     },
10348     
10349     /**
10350      * Case insensitive string
10351      * @param {Mixed} s The value being converted
10352      * @return {String} The comparison value
10353      */
10354     asUCString : function(s) {
10355         return String(s).toUpperCase();
10356     },
10357     
10358     /**
10359      * Date sorting
10360      * @param {Mixed} s The value being converted
10361      * @return {Number} The comparison value
10362      */
10363     asDate : function(s) {
10364         if(!s){
10365             return 0;
10366         }
10367         if(s instanceof Date){
10368             return s.getTime();
10369         }
10370         return Date.parse(String(s));
10371     },
10372     
10373     /**
10374      * Float sorting
10375      * @param {Mixed} s The value being converted
10376      * @return {Float} The comparison value
10377      */
10378     asFloat : function(s) {
10379         var val = parseFloat(String(s).replace(/,/g, ""));
10380         if(isNaN(val)) {
10381             val = 0;
10382         }
10383         return val;
10384     },
10385     
10386     /**
10387      * Integer sorting
10388      * @param {Mixed} s The value being converted
10389      * @return {Number} The comparison value
10390      */
10391     asInt : function(s) {
10392         var val = parseInt(String(s).replace(/,/g, ""));
10393         if(isNaN(val)) {
10394             val = 0;
10395         }
10396         return val;
10397     }
10398 };/*
10399  * Based on:
10400  * Ext JS Library 1.1.1
10401  * Copyright(c) 2006-2007, Ext JS, LLC.
10402  *
10403  * Originally Released Under LGPL - original licence link has changed is not relivant.
10404  *
10405  * Fork - LGPL
10406  * <script type="text/javascript">
10407  */
10408
10409 /**
10410 * @class Roo.data.Record
10411  * Instances of this class encapsulate both record <em>definition</em> information, and record
10412  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10413  * to access Records cached in an {@link Roo.data.Store} object.<br>
10414  * <p>
10415  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10416  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10417  * objects.<br>
10418  * <p>
10419  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10420  * @constructor
10421  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10422  * {@link #create}. The parameters are the same.
10423  * @param {Array} data An associative Array of data values keyed by the field name.
10424  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10425  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10426  * not specified an integer id is generated.
10427  */
10428 Roo.data.Record = function(data, id){
10429     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10430     this.data = data;
10431 };
10432
10433 /**
10434  * Generate a constructor for a specific record layout.
10435  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10436  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10437  * Each field definition object may contain the following properties: <ul>
10438  * <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,
10439  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10440  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10441  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10442  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10443  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10444  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10445  * this may be omitted.</p></li>
10446  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10447  * <ul><li>auto (Default, implies no conversion)</li>
10448  * <li>string</li>
10449  * <li>int</li>
10450  * <li>float</li>
10451  * <li>boolean</li>
10452  * <li>date</li></ul></p></li>
10453  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10454  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10455  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10456  * by the Reader into an object that will be stored in the Record. It is passed the
10457  * following parameters:<ul>
10458  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10459  * </ul></p></li>
10460  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10461  * </ul>
10462  * <br>usage:<br><pre><code>
10463 var TopicRecord = Roo.data.Record.create(
10464     {name: 'title', mapping: 'topic_title'},
10465     {name: 'author', mapping: 'username'},
10466     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10467     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10468     {name: 'lastPoster', mapping: 'user2'},
10469     {name: 'excerpt', mapping: 'post_text'}
10470 );
10471
10472 var myNewRecord = new TopicRecord({
10473     title: 'Do my job please',
10474     author: 'noobie',
10475     totalPosts: 1,
10476     lastPost: new Date(),
10477     lastPoster: 'Animal',
10478     excerpt: 'No way dude!'
10479 });
10480 myStore.add(myNewRecord);
10481 </code></pre>
10482  * @method create
10483  * @static
10484  */
10485 Roo.data.Record.create = function(o){
10486     var f = function(){
10487         f.superclass.constructor.apply(this, arguments);
10488     };
10489     Roo.extend(f, Roo.data.Record);
10490     var p = f.prototype;
10491     p.fields = new Roo.util.MixedCollection(false, function(field){
10492         return field.name;
10493     });
10494     for(var i = 0, len = o.length; i < len; i++){
10495         p.fields.add(new Roo.data.Field(o[i]));
10496     }
10497     f.getField = function(name){
10498         return p.fields.get(name);  
10499     };
10500     return f;
10501 };
10502
10503 Roo.data.Record.AUTO_ID = 1000;
10504 Roo.data.Record.EDIT = 'edit';
10505 Roo.data.Record.REJECT = 'reject';
10506 Roo.data.Record.COMMIT = 'commit';
10507
10508 Roo.data.Record.prototype = {
10509     /**
10510      * Readonly flag - true if this record has been modified.
10511      * @type Boolean
10512      */
10513     dirty : false,
10514     editing : false,
10515     error: null,
10516     modified: null,
10517
10518     // private
10519     join : function(store){
10520         this.store = store;
10521     },
10522
10523     /**
10524      * Set the named field to the specified value.
10525      * @param {String} name The name of the field to set.
10526      * @param {Object} value The value to set the field to.
10527      */
10528     set : function(name, value){
10529         if(this.data[name] == value){
10530             return;
10531         }
10532         this.dirty = true;
10533         if(!this.modified){
10534             this.modified = {};
10535         }
10536         if(typeof this.modified[name] == 'undefined'){
10537             this.modified[name] = this.data[name];
10538         }
10539         this.data[name] = value;
10540         if(!this.editing && this.store){
10541             this.store.afterEdit(this);
10542         }       
10543     },
10544
10545     /**
10546      * Get the value of the named field.
10547      * @param {String} name The name of the field to get the value of.
10548      * @return {Object} The value of the field.
10549      */
10550     get : function(name){
10551         return this.data[name]; 
10552     },
10553
10554     // private
10555     beginEdit : function(){
10556         this.editing = true;
10557         this.modified = {}; 
10558     },
10559
10560     // private
10561     cancelEdit : function(){
10562         this.editing = false;
10563         delete this.modified;
10564     },
10565
10566     // private
10567     endEdit : function(){
10568         this.editing = false;
10569         if(this.dirty && this.store){
10570             this.store.afterEdit(this);
10571         }
10572     },
10573
10574     /**
10575      * Usually called by the {@link Roo.data.Store} which owns the Record.
10576      * Rejects all changes made to the Record since either creation, or the last commit operation.
10577      * Modified fields are reverted to their original values.
10578      * <p>
10579      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10580      * of reject operations.
10581      */
10582     reject : function(){
10583         var m = this.modified;
10584         for(var n in m){
10585             if(typeof m[n] != "function"){
10586                 this.data[n] = m[n];
10587             }
10588         }
10589         this.dirty = false;
10590         delete this.modified;
10591         this.editing = false;
10592         if(this.store){
10593             this.store.afterReject(this);
10594         }
10595     },
10596
10597     /**
10598      * Usually called by the {@link Roo.data.Store} which owns the Record.
10599      * Commits all changes made to the Record since either creation, or the last commit operation.
10600      * <p>
10601      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10602      * of commit operations.
10603      */
10604     commit : function(){
10605         this.dirty = false;
10606         delete this.modified;
10607         this.editing = false;
10608         if(this.store){
10609             this.store.afterCommit(this);
10610         }
10611     },
10612
10613     // private
10614     hasError : function(){
10615         return this.error != null;
10616     },
10617
10618     // private
10619     clearError : function(){
10620         this.error = null;
10621     },
10622
10623     /**
10624      * Creates a copy of this record.
10625      * @param {String} id (optional) A new record id if you don't want to use this record's id
10626      * @return {Record}
10627      */
10628     copy : function(newId) {
10629         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10630     }
10631 };/*
10632  * Based on:
10633  * Ext JS Library 1.1.1
10634  * Copyright(c) 2006-2007, Ext JS, LLC.
10635  *
10636  * Originally Released Under LGPL - original licence link has changed is not relivant.
10637  *
10638  * Fork - LGPL
10639  * <script type="text/javascript">
10640  */
10641
10642
10643
10644 /**
10645  * @class Roo.data.Store
10646  * @extends Roo.util.Observable
10647  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10648  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10649  * <p>
10650  * 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
10651  * has no knowledge of the format of the data returned by the Proxy.<br>
10652  * <p>
10653  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10654  * instances from the data object. These records are cached and made available through accessor functions.
10655  * @constructor
10656  * Creates a new Store.
10657  * @param {Object} config A config object containing the objects needed for the Store to access data,
10658  * and read the data into Records.
10659  */
10660 Roo.data.Store = function(config){
10661     this.data = new Roo.util.MixedCollection(false);
10662     this.data.getKey = function(o){
10663         return o.id;
10664     };
10665     this.baseParams = {};
10666     // private
10667     this.paramNames = {
10668         "start" : "start",
10669         "limit" : "limit",
10670         "sort" : "sort",
10671         "dir" : "dir",
10672         "multisort" : "_multisort"
10673     };
10674
10675     if(config && config.data){
10676         this.inlineData = config.data;
10677         delete config.data;
10678     }
10679
10680     Roo.apply(this, config);
10681     
10682     if(this.reader){ // reader passed
10683         this.reader = Roo.factory(this.reader, Roo.data);
10684         this.reader.xmodule = this.xmodule || false;
10685         if(!this.recordType){
10686             this.recordType = this.reader.recordType;
10687         }
10688         if(this.reader.onMetaChange){
10689             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10690         }
10691     }
10692
10693     if(this.recordType){
10694         this.fields = this.recordType.prototype.fields;
10695     }
10696     this.modified = [];
10697
10698     this.addEvents({
10699         /**
10700          * @event datachanged
10701          * Fires when the data cache has changed, and a widget which is using this Store
10702          * as a Record cache should refresh its view.
10703          * @param {Store} this
10704          */
10705         datachanged : true,
10706         /**
10707          * @event metachange
10708          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10709          * @param {Store} this
10710          * @param {Object} meta The JSON metadata
10711          */
10712         metachange : true,
10713         /**
10714          * @event add
10715          * Fires when Records have been added to the Store
10716          * @param {Store} this
10717          * @param {Roo.data.Record[]} records The array of Records added
10718          * @param {Number} index The index at which the record(s) were added
10719          */
10720         add : true,
10721         /**
10722          * @event remove
10723          * Fires when a Record has been removed from the Store
10724          * @param {Store} this
10725          * @param {Roo.data.Record} record The Record that was removed
10726          * @param {Number} index The index at which the record was removed
10727          */
10728         remove : true,
10729         /**
10730          * @event update
10731          * Fires when a Record has been updated
10732          * @param {Store} this
10733          * @param {Roo.data.Record} record The Record that was updated
10734          * @param {String} operation The update operation being performed.  Value may be one of:
10735          * <pre><code>
10736  Roo.data.Record.EDIT
10737  Roo.data.Record.REJECT
10738  Roo.data.Record.COMMIT
10739          * </code></pre>
10740          */
10741         update : true,
10742         /**
10743          * @event clear
10744          * Fires when the data cache has been cleared.
10745          * @param {Store} this
10746          */
10747         clear : true,
10748         /**
10749          * @event beforeload
10750          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10751          * the load action will be canceled.
10752          * @param {Store} this
10753          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10754          */
10755         beforeload : true,
10756         /**
10757          * @event beforeloadadd
10758          * Fires after a new set of Records has been loaded.
10759          * @param {Store} this
10760          * @param {Roo.data.Record[]} records The Records that were loaded
10761          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10762          */
10763         beforeloadadd : true,
10764         /**
10765          * @event load
10766          * Fires after a new set of Records has been loaded, before they are added to the store.
10767          * @param {Store} this
10768          * @param {Roo.data.Record[]} records The Records that were loaded
10769          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10770          * @params {Object} return from reader
10771          */
10772         load : true,
10773         /**
10774          * @event loadexception
10775          * Fires if an exception occurs in the Proxy during loading.
10776          * Called with the signature of the Proxy's "loadexception" event.
10777          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10778          * 
10779          * @param {Proxy} 
10780          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10781          * @param {Object} load options 
10782          * @param {Object} jsonData from your request (normally this contains the Exception)
10783          */
10784         loadexception : true
10785     });
10786     
10787     if(this.proxy){
10788         this.proxy = Roo.factory(this.proxy, Roo.data);
10789         this.proxy.xmodule = this.xmodule || false;
10790         this.relayEvents(this.proxy,  ["loadexception"]);
10791     }
10792     this.sortToggle = {};
10793     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10794
10795     Roo.data.Store.superclass.constructor.call(this);
10796
10797     if(this.inlineData){
10798         this.loadData(this.inlineData);
10799         delete this.inlineData;
10800     }
10801 };
10802
10803 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10804      /**
10805     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10806     * without a remote query - used by combo/forms at present.
10807     */
10808     
10809     /**
10810     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10811     */
10812     /**
10813     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10814     */
10815     /**
10816     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10817     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10818     */
10819     /**
10820     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10821     * on any HTTP request
10822     */
10823     /**
10824     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10825     */
10826     /**
10827     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10828     */
10829     multiSort: false,
10830     /**
10831     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10832     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10833     */
10834     remoteSort : false,
10835
10836     /**
10837     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10838      * loaded or when a record is removed. (defaults to false).
10839     */
10840     pruneModifiedRecords : false,
10841
10842     // private
10843     lastOptions : null,
10844
10845     /**
10846      * Add Records to the Store and fires the add event.
10847      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10848      */
10849     add : function(records){
10850         records = [].concat(records);
10851         for(var i = 0, len = records.length; i < len; i++){
10852             records[i].join(this);
10853         }
10854         var index = this.data.length;
10855         this.data.addAll(records);
10856         this.fireEvent("add", this, records, index);
10857     },
10858
10859     /**
10860      * Remove a Record from the Store and fires the remove event.
10861      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10862      */
10863     remove : function(record){
10864         var index = this.data.indexOf(record);
10865         this.data.removeAt(index);
10866         if(this.pruneModifiedRecords){
10867             this.modified.remove(record);
10868         }
10869         this.fireEvent("remove", this, record, index);
10870     },
10871
10872     /**
10873      * Remove all Records from the Store and fires the clear event.
10874      */
10875     removeAll : function(){
10876         this.data.clear();
10877         if(this.pruneModifiedRecords){
10878             this.modified = [];
10879         }
10880         this.fireEvent("clear", this);
10881     },
10882
10883     /**
10884      * Inserts Records to the Store at the given index and fires the add event.
10885      * @param {Number} index The start index at which to insert the passed Records.
10886      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10887      */
10888     insert : function(index, records){
10889         records = [].concat(records);
10890         for(var i = 0, len = records.length; i < len; i++){
10891             this.data.insert(index, records[i]);
10892             records[i].join(this);
10893         }
10894         this.fireEvent("add", this, records, index);
10895     },
10896
10897     /**
10898      * Get the index within the cache of the passed Record.
10899      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10900      * @return {Number} The index of the passed Record. Returns -1 if not found.
10901      */
10902     indexOf : function(record){
10903         return this.data.indexOf(record);
10904     },
10905
10906     /**
10907      * Get the index within the cache of the Record with the passed id.
10908      * @param {String} id The id of the Record to find.
10909      * @return {Number} The index of the Record. Returns -1 if not found.
10910      */
10911     indexOfId : function(id){
10912         return this.data.indexOfKey(id);
10913     },
10914
10915     /**
10916      * Get the Record with the specified id.
10917      * @param {String} id The id of the Record to find.
10918      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10919      */
10920     getById : function(id){
10921         return this.data.key(id);
10922     },
10923
10924     /**
10925      * Get the Record at the specified index.
10926      * @param {Number} index The index of the Record to find.
10927      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10928      */
10929     getAt : function(index){
10930         return this.data.itemAt(index);
10931     },
10932
10933     /**
10934      * Returns a range of Records between specified indices.
10935      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10936      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10937      * @return {Roo.data.Record[]} An array of Records
10938      */
10939     getRange : function(start, end){
10940         return this.data.getRange(start, end);
10941     },
10942
10943     // private
10944     storeOptions : function(o){
10945         o = Roo.apply({}, o);
10946         delete o.callback;
10947         delete o.scope;
10948         this.lastOptions = o;
10949     },
10950
10951     /**
10952      * Loads the Record cache from the configured Proxy using the configured Reader.
10953      * <p>
10954      * If using remote paging, then the first load call must specify the <em>start</em>
10955      * and <em>limit</em> properties in the options.params property to establish the initial
10956      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10957      * <p>
10958      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10959      * and this call will return before the new data has been loaded. Perform any post-processing
10960      * in a callback function, or in a "load" event handler.</strong>
10961      * <p>
10962      * @param {Object} options An object containing properties which control loading options:<ul>
10963      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10964      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10965      * passed the following arguments:<ul>
10966      * <li>r : Roo.data.Record[]</li>
10967      * <li>options: Options object from the load call</li>
10968      * <li>success: Boolean success indicator</li></ul></li>
10969      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10970      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10971      * </ul>
10972      */
10973     load : function(options){
10974         options = options || {};
10975         if(this.fireEvent("beforeload", this, options) !== false){
10976             this.storeOptions(options);
10977             var p = Roo.apply(options.params || {}, this.baseParams);
10978             // if meta was not loaded from remote source.. try requesting it.
10979             if (!this.reader.metaFromRemote) {
10980                 p._requestMeta = 1;
10981             }
10982             if(this.sortInfo && this.remoteSort){
10983                 var pn = this.paramNames;
10984                 p[pn["sort"]] = this.sortInfo.field;
10985                 p[pn["dir"]] = this.sortInfo.direction;
10986             }
10987             if (this.multiSort) {
10988                 var pn = this.paramNames;
10989                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10990             }
10991             
10992             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10993         }
10994     },
10995
10996     /**
10997      * Reloads the Record cache from the configured Proxy using the configured Reader and
10998      * the options from the last load operation performed.
10999      * @param {Object} options (optional) An object containing properties which may override the options
11000      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11001      * the most recently used options are reused).
11002      */
11003     reload : function(options){
11004         this.load(Roo.applyIf(options||{}, this.lastOptions));
11005     },
11006
11007     // private
11008     // Called as a callback by the Reader during a load operation.
11009     loadRecords : function(o, options, success){
11010         if(!o || success === false){
11011             if(success !== false){
11012                 this.fireEvent("load", this, [], options, o);
11013             }
11014             if(options.callback){
11015                 options.callback.call(options.scope || this, [], options, false);
11016             }
11017             return;
11018         }
11019         // if data returned failure - throw an exception.
11020         if (o.success === false) {
11021             // show a message if no listener is registered.
11022             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11023                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11024             }
11025             // loadmask wil be hooked into this..
11026             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11027             return;
11028         }
11029         var r = o.records, t = o.totalRecords || r.length;
11030         
11031         this.fireEvent("beforeloadadd", this, r, options, o);
11032         
11033         if(!options || options.add !== true){
11034             if(this.pruneModifiedRecords){
11035                 this.modified = [];
11036             }
11037             for(var i = 0, len = r.length; i < len; i++){
11038                 r[i].join(this);
11039             }
11040             if(this.snapshot){
11041                 this.data = this.snapshot;
11042                 delete this.snapshot;
11043             }
11044             this.data.clear();
11045             this.data.addAll(r);
11046             this.totalLength = t;
11047             this.applySort();
11048             this.fireEvent("datachanged", this);
11049         }else{
11050             this.totalLength = Math.max(t, this.data.length+r.length);
11051             this.add(r);
11052         }
11053         
11054         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11055                 
11056             var e = new Roo.data.Record({});
11057
11058             e.set(this.parent.displayField, this.parent.emptyTitle);
11059             e.set(this.parent.valueField, '');
11060
11061             this.insert(0, e);
11062         }
11063             
11064         this.fireEvent("load", this, r, options, o);
11065         if(options.callback){
11066             options.callback.call(options.scope || this, r, options, true);
11067         }
11068     },
11069
11070
11071     /**
11072      * Loads data from a passed data block. A Reader which understands the format of the data
11073      * must have been configured in the constructor.
11074      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11075      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11076      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11077      */
11078     loadData : function(o, append){
11079         var r = this.reader.readRecords(o);
11080         this.loadRecords(r, {add: append}, true);
11081     },
11082
11083     /**
11084      * Gets the number of cached records.
11085      * <p>
11086      * <em>If using paging, this may not be the total size of the dataset. If the data object
11087      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11088      * the data set size</em>
11089      */
11090     getCount : function(){
11091         return this.data.length || 0;
11092     },
11093
11094     /**
11095      * Gets the total number of records in the dataset as returned by the server.
11096      * <p>
11097      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11098      * the dataset size</em>
11099      */
11100     getTotalCount : function(){
11101         return this.totalLength || 0;
11102     },
11103
11104     /**
11105      * Returns the sort state of the Store as an object with two properties:
11106      * <pre><code>
11107  field {String} The name of the field by which the Records are sorted
11108  direction {String} The sort order, "ASC" or "DESC"
11109      * </code></pre>
11110      */
11111     getSortState : function(){
11112         return this.sortInfo;
11113     },
11114
11115     // private
11116     applySort : function(){
11117         if(this.sortInfo && !this.remoteSort){
11118             var s = this.sortInfo, f = s.field;
11119             var st = this.fields.get(f).sortType;
11120             var fn = function(r1, r2){
11121                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11122                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11123             };
11124             this.data.sort(s.direction, fn);
11125             if(this.snapshot && this.snapshot != this.data){
11126                 this.snapshot.sort(s.direction, fn);
11127             }
11128         }
11129     },
11130
11131     /**
11132      * Sets the default sort column and order to be used by the next load operation.
11133      * @param {String} fieldName The name of the field to sort by.
11134      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11135      */
11136     setDefaultSort : function(field, dir){
11137         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11138     },
11139
11140     /**
11141      * Sort the Records.
11142      * If remote sorting is used, the sort is performed on the server, and the cache is
11143      * reloaded. If local sorting is used, the cache is sorted internally.
11144      * @param {String} fieldName The name of the field to sort by.
11145      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11146      */
11147     sort : function(fieldName, dir){
11148         var f = this.fields.get(fieldName);
11149         if(!dir){
11150             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11151             
11152             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11153                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11154             }else{
11155                 dir = f.sortDir;
11156             }
11157         }
11158         this.sortToggle[f.name] = dir;
11159         this.sortInfo = {field: f.name, direction: dir};
11160         if(!this.remoteSort){
11161             this.applySort();
11162             this.fireEvent("datachanged", this);
11163         }else{
11164             this.load(this.lastOptions);
11165         }
11166     },
11167
11168     /**
11169      * Calls the specified function for each of the Records in the cache.
11170      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11171      * Returning <em>false</em> aborts and exits the iteration.
11172      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11173      */
11174     each : function(fn, scope){
11175         this.data.each(fn, scope);
11176     },
11177
11178     /**
11179      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11180      * (e.g., during paging).
11181      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11182      */
11183     getModifiedRecords : function(){
11184         return this.modified;
11185     },
11186
11187     // private
11188     createFilterFn : function(property, value, anyMatch){
11189         if(!value.exec){ // not a regex
11190             value = String(value);
11191             if(value.length == 0){
11192                 return false;
11193             }
11194             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11195         }
11196         return function(r){
11197             return value.test(r.data[property]);
11198         };
11199     },
11200
11201     /**
11202      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11203      * @param {String} property A field on your records
11204      * @param {Number} start The record index to start at (defaults to 0)
11205      * @param {Number} end The last record index to include (defaults to length - 1)
11206      * @return {Number} The sum
11207      */
11208     sum : function(property, start, end){
11209         var rs = this.data.items, v = 0;
11210         start = start || 0;
11211         end = (end || end === 0) ? end : rs.length-1;
11212
11213         for(var i = start; i <= end; i++){
11214             v += (rs[i].data[property] || 0);
11215         }
11216         return v;
11217     },
11218
11219     /**
11220      * Filter the records by a specified property.
11221      * @param {String} field A field on your records
11222      * @param {String/RegExp} value Either a string that the field
11223      * should start with or a RegExp to test against the field
11224      * @param {Boolean} anyMatch True to match any part not just the beginning
11225      */
11226     filter : function(property, value, anyMatch){
11227         var fn = this.createFilterFn(property, value, anyMatch);
11228         return fn ? this.filterBy(fn) : this.clearFilter();
11229     },
11230
11231     /**
11232      * Filter by a function. The specified function will be called with each
11233      * record in this data source. If the function returns true the record is included,
11234      * otherwise it is filtered.
11235      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11236      * @param {Object} scope (optional) The scope of the function (defaults to this)
11237      */
11238     filterBy : function(fn, scope){
11239         this.snapshot = this.snapshot || this.data;
11240         this.data = this.queryBy(fn, scope||this);
11241         this.fireEvent("datachanged", this);
11242     },
11243
11244     /**
11245      * Query the records by a specified property.
11246      * @param {String} field A field on your records
11247      * @param {String/RegExp} value Either a string that the field
11248      * should start with or a RegExp to test against the field
11249      * @param {Boolean} anyMatch True to match any part not just the beginning
11250      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11251      */
11252     query : function(property, value, anyMatch){
11253         var fn = this.createFilterFn(property, value, anyMatch);
11254         return fn ? this.queryBy(fn) : this.data.clone();
11255     },
11256
11257     /**
11258      * Query by a function. The specified function will be called with each
11259      * record in this data source. If the function returns true the record is included
11260      * in the results.
11261      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11262      * @param {Object} scope (optional) The scope of the function (defaults to this)
11263       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11264      **/
11265     queryBy : function(fn, scope){
11266         var data = this.snapshot || this.data;
11267         return data.filterBy(fn, scope||this);
11268     },
11269
11270     /**
11271      * Collects unique values for a particular dataIndex from this store.
11272      * @param {String} dataIndex The property to collect
11273      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11274      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11275      * @return {Array} An array of the unique values
11276      **/
11277     collect : function(dataIndex, allowNull, bypassFilter){
11278         var d = (bypassFilter === true && this.snapshot) ?
11279                 this.snapshot.items : this.data.items;
11280         var v, sv, r = [], l = {};
11281         for(var i = 0, len = d.length; i < len; i++){
11282             v = d[i].data[dataIndex];
11283             sv = String(v);
11284             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11285                 l[sv] = true;
11286                 r[r.length] = v;
11287             }
11288         }
11289         return r;
11290     },
11291
11292     /**
11293      * Revert to a view of the Record cache with no filtering applied.
11294      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11295      */
11296     clearFilter : function(suppressEvent){
11297         if(this.snapshot && this.snapshot != this.data){
11298             this.data = this.snapshot;
11299             delete this.snapshot;
11300             if(suppressEvent !== true){
11301                 this.fireEvent("datachanged", this);
11302             }
11303         }
11304     },
11305
11306     // private
11307     afterEdit : function(record){
11308         if(this.modified.indexOf(record) == -1){
11309             this.modified.push(record);
11310         }
11311         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11312     },
11313     
11314     // private
11315     afterReject : function(record){
11316         this.modified.remove(record);
11317         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11318     },
11319
11320     // private
11321     afterCommit : function(record){
11322         this.modified.remove(record);
11323         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11324     },
11325
11326     /**
11327      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11328      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11329      */
11330     commitChanges : function(){
11331         var m = this.modified.slice(0);
11332         this.modified = [];
11333         for(var i = 0, len = m.length; i < len; i++){
11334             m[i].commit();
11335         }
11336     },
11337
11338     /**
11339      * Cancel outstanding changes on all changed records.
11340      */
11341     rejectChanges : function(){
11342         var m = this.modified.slice(0);
11343         this.modified = [];
11344         for(var i = 0, len = m.length; i < len; i++){
11345             m[i].reject();
11346         }
11347     },
11348
11349     onMetaChange : function(meta, rtype, o){
11350         this.recordType = rtype;
11351         this.fields = rtype.prototype.fields;
11352         delete this.snapshot;
11353         this.sortInfo = meta.sortInfo || this.sortInfo;
11354         this.modified = [];
11355         this.fireEvent('metachange', this, this.reader.meta);
11356     },
11357     
11358     moveIndex : function(data, type)
11359     {
11360         var index = this.indexOf(data);
11361         
11362         var newIndex = index + type;
11363         
11364         this.remove(data);
11365         
11366         this.insert(newIndex, data);
11367         
11368     }
11369 });/*
11370  * Based on:
11371  * Ext JS Library 1.1.1
11372  * Copyright(c) 2006-2007, Ext JS, LLC.
11373  *
11374  * Originally Released Under LGPL - original licence link has changed is not relivant.
11375  *
11376  * Fork - LGPL
11377  * <script type="text/javascript">
11378  */
11379
11380 /**
11381  * @class Roo.data.SimpleStore
11382  * @extends Roo.data.Store
11383  * Small helper class to make creating Stores from Array data easier.
11384  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11385  * @cfg {Array} fields An array of field definition objects, or field name strings.
11386  * @cfg {Array} data The multi-dimensional array of data
11387  * @constructor
11388  * @param {Object} config
11389  */
11390 Roo.data.SimpleStore = function(config){
11391     Roo.data.SimpleStore.superclass.constructor.call(this, {
11392         isLocal : true,
11393         reader: new Roo.data.ArrayReader({
11394                 id: config.id
11395             },
11396             Roo.data.Record.create(config.fields)
11397         ),
11398         proxy : new Roo.data.MemoryProxy(config.data)
11399     });
11400     this.load();
11401 };
11402 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11403  * Based on:
11404  * Ext JS Library 1.1.1
11405  * Copyright(c) 2006-2007, Ext JS, LLC.
11406  *
11407  * Originally Released Under LGPL - original licence link has changed is not relivant.
11408  *
11409  * Fork - LGPL
11410  * <script type="text/javascript">
11411  */
11412
11413 /**
11414 /**
11415  * @extends Roo.data.Store
11416  * @class Roo.data.JsonStore
11417  * Small helper class to make creating Stores for JSON data easier. <br/>
11418 <pre><code>
11419 var store = new Roo.data.JsonStore({
11420     url: 'get-images.php',
11421     root: 'images',
11422     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11423 });
11424 </code></pre>
11425  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11426  * JsonReader and HttpProxy (unless inline data is provided).</b>
11427  * @cfg {Array} fields An array of field definition objects, or field name strings.
11428  * @constructor
11429  * @param {Object} config
11430  */
11431 Roo.data.JsonStore = function(c){
11432     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11433         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11434         reader: new Roo.data.JsonReader(c, c.fields)
11435     }));
11436 };
11437 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11438  * Based on:
11439  * Ext JS Library 1.1.1
11440  * Copyright(c) 2006-2007, Ext JS, LLC.
11441  *
11442  * Originally Released Under LGPL - original licence link has changed is not relivant.
11443  *
11444  * Fork - LGPL
11445  * <script type="text/javascript">
11446  */
11447
11448  
11449 Roo.data.Field = function(config){
11450     if(typeof config == "string"){
11451         config = {name: config};
11452     }
11453     Roo.apply(this, config);
11454     
11455     if(!this.type){
11456         this.type = "auto";
11457     }
11458     
11459     var st = Roo.data.SortTypes;
11460     // named sortTypes are supported, here we look them up
11461     if(typeof this.sortType == "string"){
11462         this.sortType = st[this.sortType];
11463     }
11464     
11465     // set default sortType for strings and dates
11466     if(!this.sortType){
11467         switch(this.type){
11468             case "string":
11469                 this.sortType = st.asUCString;
11470                 break;
11471             case "date":
11472                 this.sortType = st.asDate;
11473                 break;
11474             default:
11475                 this.sortType = st.none;
11476         }
11477     }
11478
11479     // define once
11480     var stripRe = /[\$,%]/g;
11481
11482     // prebuilt conversion function for this field, instead of
11483     // switching every time we're reading a value
11484     if(!this.convert){
11485         var cv, dateFormat = this.dateFormat;
11486         switch(this.type){
11487             case "":
11488             case "auto":
11489             case undefined:
11490                 cv = function(v){ return v; };
11491                 break;
11492             case "string":
11493                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11494                 break;
11495             case "int":
11496                 cv = function(v){
11497                     return v !== undefined && v !== null && v !== '' ?
11498                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11499                     };
11500                 break;
11501             case "float":
11502                 cv = function(v){
11503                     return v !== undefined && v !== null && v !== '' ?
11504                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11505                     };
11506                 break;
11507             case "bool":
11508             case "boolean":
11509                 cv = function(v){ return v === true || v === "true" || v == 1; };
11510                 break;
11511             case "date":
11512                 cv = function(v){
11513                     if(!v){
11514                         return '';
11515                     }
11516                     if(v instanceof Date){
11517                         return v;
11518                     }
11519                     if(dateFormat){
11520                         if(dateFormat == "timestamp"){
11521                             return new Date(v*1000);
11522                         }
11523                         return Date.parseDate(v, dateFormat);
11524                     }
11525                     var parsed = Date.parse(v);
11526                     return parsed ? new Date(parsed) : null;
11527                 };
11528              break;
11529             
11530         }
11531         this.convert = cv;
11532     }
11533 };
11534
11535 Roo.data.Field.prototype = {
11536     dateFormat: null,
11537     defaultValue: "",
11538     mapping: null,
11539     sortType : null,
11540     sortDir : "ASC"
11541 };/*
11542  * Based on:
11543  * Ext JS Library 1.1.1
11544  * Copyright(c) 2006-2007, Ext JS, LLC.
11545  *
11546  * Originally Released Under LGPL - original licence link has changed is not relivant.
11547  *
11548  * Fork - LGPL
11549  * <script type="text/javascript">
11550  */
11551  
11552 // Base class for reading structured data from a data source.  This class is intended to be
11553 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11554
11555 /**
11556  * @class Roo.data.DataReader
11557  * Base class for reading structured data from a data source.  This class is intended to be
11558  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11559  */
11560
11561 Roo.data.DataReader = function(meta, recordType){
11562     
11563     this.meta = meta;
11564     
11565     this.recordType = recordType instanceof Array ? 
11566         Roo.data.Record.create(recordType) : recordType;
11567 };
11568
11569 Roo.data.DataReader.prototype = {
11570      /**
11571      * Create an empty record
11572      * @param {Object} data (optional) - overlay some values
11573      * @return {Roo.data.Record} record created.
11574      */
11575     newRow :  function(d) {
11576         var da =  {};
11577         this.recordType.prototype.fields.each(function(c) {
11578             switch( c.type) {
11579                 case 'int' : da[c.name] = 0; break;
11580                 case 'date' : da[c.name] = new Date(); break;
11581                 case 'float' : da[c.name] = 0.0; break;
11582                 case 'boolean' : da[c.name] = false; break;
11583                 default : da[c.name] = ""; break;
11584             }
11585             
11586         });
11587         return new this.recordType(Roo.apply(da, d));
11588     }
11589     
11590 };/*
11591  * Based on:
11592  * Ext JS Library 1.1.1
11593  * Copyright(c) 2006-2007, Ext JS, LLC.
11594  *
11595  * Originally Released Under LGPL - original licence link has changed is not relivant.
11596  *
11597  * Fork - LGPL
11598  * <script type="text/javascript">
11599  */
11600
11601 /**
11602  * @class Roo.data.DataProxy
11603  * @extends Roo.data.Observable
11604  * This class is an abstract base class for implementations which provide retrieval of
11605  * unformatted data objects.<br>
11606  * <p>
11607  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11608  * (of the appropriate type which knows how to parse the data object) to provide a block of
11609  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11610  * <p>
11611  * Custom implementations must implement the load method as described in
11612  * {@link Roo.data.HttpProxy#load}.
11613  */
11614 Roo.data.DataProxy = function(){
11615     this.addEvents({
11616         /**
11617          * @event beforeload
11618          * Fires before a network request is made to retrieve a data object.
11619          * @param {Object} This DataProxy object.
11620          * @param {Object} params The params parameter to the load function.
11621          */
11622         beforeload : true,
11623         /**
11624          * @event load
11625          * Fires before the load method's callback is called.
11626          * @param {Object} This DataProxy object.
11627          * @param {Object} o The data object.
11628          * @param {Object} arg The callback argument object passed to the load function.
11629          */
11630         load : true,
11631         /**
11632          * @event loadexception
11633          * Fires if an Exception occurs during data retrieval.
11634          * @param {Object} This DataProxy object.
11635          * @param {Object} o The data object.
11636          * @param {Object} arg The callback argument object passed to the load function.
11637          * @param {Object} e The Exception.
11638          */
11639         loadexception : true
11640     });
11641     Roo.data.DataProxy.superclass.constructor.call(this);
11642 };
11643
11644 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11645
11646     /**
11647      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11648      */
11649 /*
11650  * Based on:
11651  * Ext JS Library 1.1.1
11652  * Copyright(c) 2006-2007, Ext JS, LLC.
11653  *
11654  * Originally Released Under LGPL - original licence link has changed is not relivant.
11655  *
11656  * Fork - LGPL
11657  * <script type="text/javascript">
11658  */
11659 /**
11660  * @class Roo.data.MemoryProxy
11661  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11662  * to the Reader when its load method is called.
11663  * @constructor
11664  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11665  */
11666 Roo.data.MemoryProxy = function(data){
11667     if (data.data) {
11668         data = data.data;
11669     }
11670     Roo.data.MemoryProxy.superclass.constructor.call(this);
11671     this.data = data;
11672 };
11673
11674 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11675     
11676     /**
11677      * Load data from the requested source (in this case an in-memory
11678      * data object passed to the constructor), read the data object into
11679      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11680      * process that block using the passed callback.
11681      * @param {Object} params This parameter is not used by the MemoryProxy class.
11682      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11683      * object into a block of Roo.data.Records.
11684      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11685      * The function must be passed <ul>
11686      * <li>The Record block object</li>
11687      * <li>The "arg" argument from the load function</li>
11688      * <li>A boolean success indicator</li>
11689      * </ul>
11690      * @param {Object} scope The scope in which to call the callback
11691      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11692      */
11693     load : function(params, reader, callback, scope, arg){
11694         params = params || {};
11695         var result;
11696         try {
11697             result = reader.readRecords(this.data);
11698         }catch(e){
11699             this.fireEvent("loadexception", this, arg, null, e);
11700             callback.call(scope, null, arg, false);
11701             return;
11702         }
11703         callback.call(scope, result, arg, true);
11704     },
11705     
11706     // private
11707     update : function(params, records){
11708         
11709     }
11710 });/*
11711  * Based on:
11712  * Ext JS Library 1.1.1
11713  * Copyright(c) 2006-2007, Ext JS, LLC.
11714  *
11715  * Originally Released Under LGPL - original licence link has changed is not relivant.
11716  *
11717  * Fork - LGPL
11718  * <script type="text/javascript">
11719  */
11720 /**
11721  * @class Roo.data.HttpProxy
11722  * @extends Roo.data.DataProxy
11723  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11724  * configured to reference a certain URL.<br><br>
11725  * <p>
11726  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11727  * from which the running page was served.<br><br>
11728  * <p>
11729  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11730  * <p>
11731  * Be aware that to enable the browser to parse an XML document, the server must set
11732  * the Content-Type header in the HTTP response to "text/xml".
11733  * @constructor
11734  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11735  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11736  * will be used to make the request.
11737  */
11738 Roo.data.HttpProxy = function(conn){
11739     Roo.data.HttpProxy.superclass.constructor.call(this);
11740     // is conn a conn config or a real conn?
11741     this.conn = conn;
11742     this.useAjax = !conn || !conn.events;
11743   
11744 };
11745
11746 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11747     // thse are take from connection...
11748     
11749     /**
11750      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11751      */
11752     /**
11753      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11754      * extra parameters to each request made by this object. (defaults to undefined)
11755      */
11756     /**
11757      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11758      *  to each request made by this object. (defaults to undefined)
11759      */
11760     /**
11761      * @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)
11762      */
11763     /**
11764      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11765      */
11766      /**
11767      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11768      * @type Boolean
11769      */
11770   
11771
11772     /**
11773      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11774      * @type Boolean
11775      */
11776     /**
11777      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11778      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11779      * a finer-grained basis than the DataProxy events.
11780      */
11781     getConnection : function(){
11782         return this.useAjax ? Roo.Ajax : this.conn;
11783     },
11784
11785     /**
11786      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11787      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11788      * process that block using the passed callback.
11789      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11790      * for the request to the remote server.
11791      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11792      * object into a block of Roo.data.Records.
11793      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11794      * The function must be passed <ul>
11795      * <li>The Record block object</li>
11796      * <li>The "arg" argument from the load function</li>
11797      * <li>A boolean success indicator</li>
11798      * </ul>
11799      * @param {Object} scope The scope in which to call the callback
11800      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11801      */
11802     load : function(params, reader, callback, scope, arg){
11803         if(this.fireEvent("beforeload", this, params) !== false){
11804             var  o = {
11805                 params : params || {},
11806                 request: {
11807                     callback : callback,
11808                     scope : scope,
11809                     arg : arg
11810                 },
11811                 reader: reader,
11812                 callback : this.loadResponse,
11813                 scope: this
11814             };
11815             if(this.useAjax){
11816                 Roo.applyIf(o, this.conn);
11817                 if(this.activeRequest){
11818                     Roo.Ajax.abort(this.activeRequest);
11819                 }
11820                 this.activeRequest = Roo.Ajax.request(o);
11821             }else{
11822                 this.conn.request(o);
11823             }
11824         }else{
11825             callback.call(scope||this, null, arg, false);
11826         }
11827     },
11828
11829     // private
11830     loadResponse : function(o, success, response){
11831         delete this.activeRequest;
11832         if(!success){
11833             this.fireEvent("loadexception", this, o, response);
11834             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11835             return;
11836         }
11837         var result;
11838         try {
11839             result = o.reader.read(response);
11840         }catch(e){
11841             this.fireEvent("loadexception", this, o, response, e);
11842             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11843             return;
11844         }
11845         
11846         this.fireEvent("load", this, o, o.request.arg);
11847         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11848     },
11849
11850     // private
11851     update : function(dataSet){
11852
11853     },
11854
11855     // private
11856     updateResponse : function(dataSet){
11857
11858     }
11859 });/*
11860  * Based on:
11861  * Ext JS Library 1.1.1
11862  * Copyright(c) 2006-2007, Ext JS, LLC.
11863  *
11864  * Originally Released Under LGPL - original licence link has changed is not relivant.
11865  *
11866  * Fork - LGPL
11867  * <script type="text/javascript">
11868  */
11869
11870 /**
11871  * @class Roo.data.ScriptTagProxy
11872  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11873  * other than the originating domain of the running page.<br><br>
11874  * <p>
11875  * <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
11876  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11877  * <p>
11878  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11879  * source code that is used as the source inside a &lt;script> tag.<br><br>
11880  * <p>
11881  * In order for the browser to process the returned data, the server must wrap the data object
11882  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11883  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11884  * depending on whether the callback name was passed:
11885  * <p>
11886  * <pre><code>
11887 boolean scriptTag = false;
11888 String cb = request.getParameter("callback");
11889 if (cb != null) {
11890     scriptTag = true;
11891     response.setContentType("text/javascript");
11892 } else {
11893     response.setContentType("application/x-json");
11894 }
11895 Writer out = response.getWriter();
11896 if (scriptTag) {
11897     out.write(cb + "(");
11898 }
11899 out.print(dataBlock.toJsonString());
11900 if (scriptTag) {
11901     out.write(");");
11902 }
11903 </pre></code>
11904  *
11905  * @constructor
11906  * @param {Object} config A configuration object.
11907  */
11908 Roo.data.ScriptTagProxy = function(config){
11909     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11910     Roo.apply(this, config);
11911     this.head = document.getElementsByTagName("head")[0];
11912 };
11913
11914 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11915
11916 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11917     /**
11918      * @cfg {String} url The URL from which to request the data object.
11919      */
11920     /**
11921      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11922      */
11923     timeout : 30000,
11924     /**
11925      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11926      * the server the name of the callback function set up by the load call to process the returned data object.
11927      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11928      * javascript output which calls this named function passing the data object as its only parameter.
11929      */
11930     callbackParam : "callback",
11931     /**
11932      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11933      * name to the request.
11934      */
11935     nocache : true,
11936
11937     /**
11938      * Load data from the configured URL, read the data object into
11939      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11940      * process that block using the passed callback.
11941      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11942      * for the request to the remote server.
11943      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11944      * object into a block of Roo.data.Records.
11945      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11946      * The function must be passed <ul>
11947      * <li>The Record block object</li>
11948      * <li>The "arg" argument from the load function</li>
11949      * <li>A boolean success indicator</li>
11950      * </ul>
11951      * @param {Object} scope The scope in which to call the callback
11952      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11953      */
11954     load : function(params, reader, callback, scope, arg){
11955         if(this.fireEvent("beforeload", this, params) !== false){
11956
11957             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11958
11959             var url = this.url;
11960             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11961             if(this.nocache){
11962                 url += "&_dc=" + (new Date().getTime());
11963             }
11964             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11965             var trans = {
11966                 id : transId,
11967                 cb : "stcCallback"+transId,
11968                 scriptId : "stcScript"+transId,
11969                 params : params,
11970                 arg : arg,
11971                 url : url,
11972                 callback : callback,
11973                 scope : scope,
11974                 reader : reader
11975             };
11976             var conn = this;
11977
11978             window[trans.cb] = function(o){
11979                 conn.handleResponse(o, trans);
11980             };
11981
11982             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11983
11984             if(this.autoAbort !== false){
11985                 this.abort();
11986             }
11987
11988             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11989
11990             var script = document.createElement("script");
11991             script.setAttribute("src", url);
11992             script.setAttribute("type", "text/javascript");
11993             script.setAttribute("id", trans.scriptId);
11994             this.head.appendChild(script);
11995
11996             this.trans = trans;
11997         }else{
11998             callback.call(scope||this, null, arg, false);
11999         }
12000     },
12001
12002     // private
12003     isLoading : function(){
12004         return this.trans ? true : false;
12005     },
12006
12007     /**
12008      * Abort the current server request.
12009      */
12010     abort : function(){
12011         if(this.isLoading()){
12012             this.destroyTrans(this.trans);
12013         }
12014     },
12015
12016     // private
12017     destroyTrans : function(trans, isLoaded){
12018         this.head.removeChild(document.getElementById(trans.scriptId));
12019         clearTimeout(trans.timeoutId);
12020         if(isLoaded){
12021             window[trans.cb] = undefined;
12022             try{
12023                 delete window[trans.cb];
12024             }catch(e){}
12025         }else{
12026             // if hasn't been loaded, wait for load to remove it to prevent script error
12027             window[trans.cb] = function(){
12028                 window[trans.cb] = undefined;
12029                 try{
12030                     delete window[trans.cb];
12031                 }catch(e){}
12032             };
12033         }
12034     },
12035
12036     // private
12037     handleResponse : function(o, trans){
12038         this.trans = false;
12039         this.destroyTrans(trans, true);
12040         var result;
12041         try {
12042             result = trans.reader.readRecords(o);
12043         }catch(e){
12044             this.fireEvent("loadexception", this, o, trans.arg, e);
12045             trans.callback.call(trans.scope||window, null, trans.arg, false);
12046             return;
12047         }
12048         this.fireEvent("load", this, o, trans.arg);
12049         trans.callback.call(trans.scope||window, result, trans.arg, true);
12050     },
12051
12052     // private
12053     handleFailure : function(trans){
12054         this.trans = false;
12055         this.destroyTrans(trans, false);
12056         this.fireEvent("loadexception", this, null, trans.arg);
12057         trans.callback.call(trans.scope||window, null, trans.arg, false);
12058     }
12059 });/*
12060  * Based on:
12061  * Ext JS Library 1.1.1
12062  * Copyright(c) 2006-2007, Ext JS, LLC.
12063  *
12064  * Originally Released Under LGPL - original licence link has changed is not relivant.
12065  *
12066  * Fork - LGPL
12067  * <script type="text/javascript">
12068  */
12069
12070 /**
12071  * @class Roo.data.JsonReader
12072  * @extends Roo.data.DataReader
12073  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12074  * based on mappings in a provided Roo.data.Record constructor.
12075  * 
12076  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12077  * in the reply previously. 
12078  * 
12079  * <p>
12080  * Example code:
12081  * <pre><code>
12082 var RecordDef = Roo.data.Record.create([
12083     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12084     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12085 ]);
12086 var myReader = new Roo.data.JsonReader({
12087     totalProperty: "results",    // The property which contains the total dataset size (optional)
12088     root: "rows",                // The property which contains an Array of row objects
12089     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12090 }, RecordDef);
12091 </code></pre>
12092  * <p>
12093  * This would consume a JSON file like this:
12094  * <pre><code>
12095 { 'results': 2, 'rows': [
12096     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12097     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12098 }
12099 </code></pre>
12100  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12101  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12102  * paged from the remote server.
12103  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12104  * @cfg {String} root name of the property which contains the Array of row objects.
12105  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12106  * @cfg {Array} fields Array of field definition objects
12107  * @constructor
12108  * Create a new JsonReader
12109  * @param {Object} meta Metadata configuration options
12110  * @param {Object} recordType Either an Array of field definition objects,
12111  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12112  */
12113 Roo.data.JsonReader = function(meta, recordType){
12114     
12115     meta = meta || {};
12116     // set some defaults:
12117     Roo.applyIf(meta, {
12118         totalProperty: 'total',
12119         successProperty : 'success',
12120         root : 'data',
12121         id : 'id'
12122     });
12123     
12124     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12125 };
12126 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12127     
12128     /**
12129      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12130      * Used by Store query builder to append _requestMeta to params.
12131      * 
12132      */
12133     metaFromRemote : false,
12134     /**
12135      * This method is only used by a DataProxy which has retrieved data from a remote server.
12136      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12137      * @return {Object} data A data block which is used by an Roo.data.Store object as
12138      * a cache of Roo.data.Records.
12139      */
12140     read : function(response){
12141         var json = response.responseText;
12142        
12143         var o = /* eval:var:o */ eval("("+json+")");
12144         if(!o) {
12145             throw {message: "JsonReader.read: Json object not found"};
12146         }
12147         
12148         if(o.metaData){
12149             
12150             delete this.ef;
12151             this.metaFromRemote = true;
12152             this.meta = o.metaData;
12153             this.recordType = Roo.data.Record.create(o.metaData.fields);
12154             this.onMetaChange(this.meta, this.recordType, o);
12155         }
12156         return this.readRecords(o);
12157     },
12158
12159     // private function a store will implement
12160     onMetaChange : function(meta, recordType, o){
12161
12162     },
12163
12164     /**
12165          * @ignore
12166          */
12167     simpleAccess: function(obj, subsc) {
12168         return obj[subsc];
12169     },
12170
12171         /**
12172          * @ignore
12173          */
12174     getJsonAccessor: function(){
12175         var re = /[\[\.]/;
12176         return function(expr) {
12177             try {
12178                 return(re.test(expr))
12179                     ? new Function("obj", "return obj." + expr)
12180                     : function(obj){
12181                         return obj[expr];
12182                     };
12183             } catch(e){}
12184             return Roo.emptyFn;
12185         };
12186     }(),
12187
12188     /**
12189      * Create a data block containing Roo.data.Records from an XML document.
12190      * @param {Object} o An object which contains an Array of row objects in the property specified
12191      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12192      * which contains the total size of the dataset.
12193      * @return {Object} data A data block which is used by an Roo.data.Store object as
12194      * a cache of Roo.data.Records.
12195      */
12196     readRecords : function(o){
12197         /**
12198          * After any data loads, the raw JSON data is available for further custom processing.
12199          * @type Object
12200          */
12201         this.o = o;
12202         var s = this.meta, Record = this.recordType,
12203             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12204
12205 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12206         if (!this.ef) {
12207             if(s.totalProperty) {
12208                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12209                 }
12210                 if(s.successProperty) {
12211                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12212                 }
12213                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12214                 if (s.id) {
12215                         var g = this.getJsonAccessor(s.id);
12216                         this.getId = function(rec) {
12217                                 var r = g(rec);  
12218                                 return (r === undefined || r === "") ? null : r;
12219                         };
12220                 } else {
12221                         this.getId = function(){return null;};
12222                 }
12223             this.ef = [];
12224             for(var jj = 0; jj < fl; jj++){
12225                 f = fi[jj];
12226                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12227                 this.ef[jj] = this.getJsonAccessor(map);
12228             }
12229         }
12230
12231         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12232         if(s.totalProperty){
12233             var vt = parseInt(this.getTotal(o), 10);
12234             if(!isNaN(vt)){
12235                 totalRecords = vt;
12236             }
12237         }
12238         if(s.successProperty){
12239             var vs = this.getSuccess(o);
12240             if(vs === false || vs === 'false'){
12241                 success = false;
12242             }
12243         }
12244         var records = [];
12245         for(var i = 0; i < c; i++){
12246                 var n = root[i];
12247             var values = {};
12248             var id = this.getId(n);
12249             for(var j = 0; j < fl; j++){
12250                 f = fi[j];
12251             var v = this.ef[j](n);
12252             if (!f.convert) {
12253                 Roo.log('missing convert for ' + f.name);
12254                 Roo.log(f);
12255                 continue;
12256             }
12257             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12258             }
12259             var record = new Record(values, id);
12260             record.json = n;
12261             records[i] = record;
12262         }
12263         return {
12264             raw : o,
12265             success : success,
12266             records : records,
12267             totalRecords : totalRecords
12268         };
12269     }
12270 });/*
12271  * Based on:
12272  * Ext JS Library 1.1.1
12273  * Copyright(c) 2006-2007, Ext JS, LLC.
12274  *
12275  * Originally Released Under LGPL - original licence link has changed is not relivant.
12276  *
12277  * Fork - LGPL
12278  * <script type="text/javascript">
12279  */
12280
12281 /**
12282  * @class Roo.data.ArrayReader
12283  * @extends Roo.data.DataReader
12284  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12285  * Each element of that Array represents a row of data fields. The
12286  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12287  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12288  * <p>
12289  * Example code:.
12290  * <pre><code>
12291 var RecordDef = Roo.data.Record.create([
12292     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12293     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12294 ]);
12295 var myReader = new Roo.data.ArrayReader({
12296     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12297 }, RecordDef);
12298 </code></pre>
12299  * <p>
12300  * This would consume an Array like this:
12301  * <pre><code>
12302 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12303   </code></pre>
12304  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12305  * @constructor
12306  * Create a new JsonReader
12307  * @param {Object} meta Metadata configuration options.
12308  * @param {Object} recordType Either an Array of field definition objects
12309  * as specified to {@link Roo.data.Record#create},
12310  * or an {@link Roo.data.Record} object
12311  * created using {@link Roo.data.Record#create}.
12312  */
12313 Roo.data.ArrayReader = function(meta, recordType){
12314     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12315 };
12316
12317 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12318     /**
12319      * Create a data block containing Roo.data.Records from an XML document.
12320      * @param {Object} o An Array of row objects which represents the dataset.
12321      * @return {Object} data A data block which is used by an Roo.data.Store object as
12322      * a cache of Roo.data.Records.
12323      */
12324     readRecords : function(o){
12325         var sid = this.meta ? this.meta.id : null;
12326         var recordType = this.recordType, fields = recordType.prototype.fields;
12327         var records = [];
12328         var root = o;
12329             for(var i = 0; i < root.length; i++){
12330                     var n = root[i];
12331                 var values = {};
12332                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12333                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12334                 var f = fields.items[j];
12335                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12336                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12337                 v = f.convert(v);
12338                 values[f.name] = v;
12339             }
12340                 var record = new recordType(values, id);
12341                 record.json = n;
12342                 records[records.length] = record;
12343             }
12344             return {
12345                 records : records,
12346                 totalRecords : records.length
12347             };
12348     }
12349 });/*
12350  * - LGPL
12351  * * 
12352  */
12353
12354 /**
12355  * @class Roo.bootstrap.ComboBox
12356  * @extends Roo.bootstrap.TriggerField
12357  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12358  * @cfg {Boolean} append (true|false) default false
12359  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12360  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12361  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12362  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12363  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12364  * @cfg {Boolean} animate default true
12365  * @cfg {Boolean} emptyResultText only for touch device
12366  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12367  * @cfg {String} emptyTitle default ''
12368  * @constructor
12369  * Create a new ComboBox.
12370  * @param {Object} config Configuration options
12371  */
12372 Roo.bootstrap.ComboBox = function(config){
12373     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12374     this.addEvents({
12375         /**
12376          * @event expand
12377          * Fires when the dropdown list is expanded
12378              * @param {Roo.bootstrap.ComboBox} combo This combo box
12379              */
12380         'expand' : true,
12381         /**
12382          * @event collapse
12383          * Fires when the dropdown list is collapsed
12384              * @param {Roo.bootstrap.ComboBox} combo This combo box
12385              */
12386         'collapse' : true,
12387         /**
12388          * @event beforeselect
12389          * Fires before a list item is selected. Return false to cancel the selection.
12390              * @param {Roo.bootstrap.ComboBox} combo This combo box
12391              * @param {Roo.data.Record} record The data record returned from the underlying store
12392              * @param {Number} index The index of the selected item in the dropdown list
12393              */
12394         'beforeselect' : true,
12395         /**
12396          * @event select
12397          * Fires when a list item is selected
12398              * @param {Roo.bootstrap.ComboBox} combo This combo box
12399              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12400              * @param {Number} index The index of the selected item in the dropdown list
12401              */
12402         'select' : true,
12403         /**
12404          * @event beforequery
12405          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12406          * The event object passed has these properties:
12407              * @param {Roo.bootstrap.ComboBox} combo This combo box
12408              * @param {String} query The query
12409              * @param {Boolean} forceAll true to force "all" query
12410              * @param {Boolean} cancel true to cancel the query
12411              * @param {Object} e The query event object
12412              */
12413         'beforequery': true,
12414          /**
12415          * @event add
12416          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12417              * @param {Roo.bootstrap.ComboBox} combo This combo box
12418              */
12419         'add' : true,
12420         /**
12421          * @event edit
12422          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12423              * @param {Roo.bootstrap.ComboBox} combo This combo box
12424              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12425              */
12426         'edit' : true,
12427         /**
12428          * @event remove
12429          * Fires when the remove value from the combobox array
12430              * @param {Roo.bootstrap.ComboBox} combo This combo box
12431              */
12432         'remove' : true,
12433         /**
12434          * @event afterremove
12435          * Fires when the remove value from the combobox array
12436              * @param {Roo.bootstrap.ComboBox} combo This combo box
12437              */
12438         'afterremove' : true,
12439         /**
12440          * @event specialfilter
12441          * Fires when specialfilter
12442             * @param {Roo.bootstrap.ComboBox} combo This combo box
12443             */
12444         'specialfilter' : true,
12445         /**
12446          * @event tick
12447          * Fires when tick the element
12448             * @param {Roo.bootstrap.ComboBox} combo This combo box
12449             */
12450         'tick' : true,
12451         /**
12452          * @event touchviewdisplay
12453          * Fires when touch view require special display (default is using displayField)
12454             * @param {Roo.bootstrap.ComboBox} combo This combo box
12455             * @param {Object} cfg set html .
12456             */
12457         'touchviewdisplay' : true
12458         
12459     });
12460     
12461     this.item = [];
12462     this.tickItems = [];
12463     
12464     this.selectedIndex = -1;
12465     if(this.mode == 'local'){
12466         if(config.queryDelay === undefined){
12467             this.queryDelay = 10;
12468         }
12469         if(config.minChars === undefined){
12470             this.minChars = 0;
12471         }
12472     }
12473 };
12474
12475 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12476      
12477     /**
12478      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12479      * rendering into an Roo.Editor, defaults to false)
12480      */
12481     /**
12482      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12483      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12484      */
12485     /**
12486      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12487      */
12488     /**
12489      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12490      * the dropdown list (defaults to undefined, with no header element)
12491      */
12492
12493      /**
12494      * @cfg {String/Roo.Template} tpl The template to use to render the output
12495      */
12496      
12497      /**
12498      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12499      */
12500     listWidth: undefined,
12501     /**
12502      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12503      * mode = 'remote' or 'text' if mode = 'local')
12504      */
12505     displayField: undefined,
12506     
12507     /**
12508      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12509      * mode = 'remote' or 'value' if mode = 'local'). 
12510      * Note: use of a valueField requires the user make a selection
12511      * in order for a value to be mapped.
12512      */
12513     valueField: undefined,
12514     /**
12515      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12516      */
12517     modalTitle : '',
12518     
12519     /**
12520      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12521      * field's data value (defaults to the underlying DOM element's name)
12522      */
12523     hiddenName: undefined,
12524     /**
12525      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12526      */
12527     listClass: '',
12528     /**
12529      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12530      */
12531     selectedClass: 'active',
12532     
12533     /**
12534      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12535      */
12536     shadow:'sides',
12537     /**
12538      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12539      * anchor positions (defaults to 'tl-bl')
12540      */
12541     listAlign: 'tl-bl?',
12542     /**
12543      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12544      */
12545     maxHeight: 300,
12546     /**
12547      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12548      * query specified by the allQuery config option (defaults to 'query')
12549      */
12550     triggerAction: 'query',
12551     /**
12552      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12553      * (defaults to 4, does not apply if editable = false)
12554      */
12555     minChars : 4,
12556     /**
12557      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12558      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12559      */
12560     typeAhead: false,
12561     /**
12562      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12563      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12564      */
12565     queryDelay: 500,
12566     /**
12567      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12568      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12569      */
12570     pageSize: 0,
12571     /**
12572      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12573      * when editable = true (defaults to false)
12574      */
12575     selectOnFocus:false,
12576     /**
12577      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12578      */
12579     queryParam: 'query',
12580     /**
12581      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12582      * when mode = 'remote' (defaults to 'Loading...')
12583      */
12584     loadingText: 'Loading...',
12585     /**
12586      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12587      */
12588     resizable: false,
12589     /**
12590      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12591      */
12592     handleHeight : 8,
12593     /**
12594      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12595      * traditional select (defaults to true)
12596      */
12597     editable: true,
12598     /**
12599      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12600      */
12601     allQuery: '',
12602     /**
12603      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12604      */
12605     mode: 'remote',
12606     /**
12607      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12608      * listWidth has a higher value)
12609      */
12610     minListWidth : 70,
12611     /**
12612      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12613      * allow the user to set arbitrary text into the field (defaults to false)
12614      */
12615     forceSelection:false,
12616     /**
12617      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12618      * if typeAhead = true (defaults to 250)
12619      */
12620     typeAheadDelay : 250,
12621     /**
12622      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12623      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12624      */
12625     valueNotFoundText : undefined,
12626     /**
12627      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12628      */
12629     blockFocus : false,
12630     
12631     /**
12632      * @cfg {Boolean} disableClear Disable showing of clear button.
12633      */
12634     disableClear : false,
12635     /**
12636      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12637      */
12638     alwaysQuery : false,
12639     
12640     /**
12641      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12642      */
12643     multiple : false,
12644     
12645     /**
12646      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12647      */
12648     invalidClass : "has-warning",
12649     
12650     /**
12651      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12652      */
12653     validClass : "has-success",
12654     
12655     /**
12656      * @cfg {Boolean} specialFilter (true|false) special filter default false
12657      */
12658     specialFilter : false,
12659     
12660     /**
12661      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12662      */
12663     mobileTouchView : true,
12664     
12665     /**
12666      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12667      */
12668     useNativeIOS : false,
12669     
12670     ios_options : false,
12671     
12672     //private
12673     addicon : false,
12674     editicon: false,
12675     
12676     page: 0,
12677     hasQuery: false,
12678     append: false,
12679     loadNext: false,
12680     autoFocus : true,
12681     tickable : false,
12682     btnPosition : 'right',
12683     triggerList : true,
12684     showToggleBtn : true,
12685     animate : true,
12686     emptyResultText: 'Empty',
12687     triggerText : 'Select',
12688     emptyTitle : '',
12689     
12690     // element that contains real text value.. (when hidden is used..)
12691     
12692     getAutoCreate : function()
12693     {   
12694         var cfg = false;
12695         //render
12696         /*
12697          * Render classic select for iso
12698          */
12699         
12700         if(Roo.isIOS && this.useNativeIOS){
12701             cfg = this.getAutoCreateNativeIOS();
12702             return cfg;
12703         }
12704         
12705         /*
12706          * Touch Devices
12707          */
12708         
12709         if(Roo.isTouch && this.mobileTouchView){
12710             cfg = this.getAutoCreateTouchView();
12711             return cfg;;
12712         }
12713         
12714         /*
12715          *  Normal ComboBox
12716          */
12717         if(!this.tickable){
12718             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12719             return cfg;
12720         }
12721         
12722         /*
12723          *  ComboBox with tickable selections
12724          */
12725              
12726         var align = this.labelAlign || this.parentLabelAlign();
12727         
12728         cfg = {
12729             cls : 'form-group roo-combobox-tickable' //input-group
12730         };
12731         
12732         var btn_text_select = '';
12733         var btn_text_done = '';
12734         var btn_text_cancel = '';
12735         
12736         if (this.btn_text_show) {
12737             btn_text_select = 'Select';
12738             btn_text_done = 'Done';
12739             btn_text_cancel = 'Cancel'; 
12740         }
12741         
12742         var buttons = {
12743             tag : 'div',
12744             cls : 'tickable-buttons',
12745             cn : [
12746                 {
12747                     tag : 'button',
12748                     type : 'button',
12749                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12750                     //html : this.triggerText
12751                     html: btn_text_select
12752                 },
12753                 {
12754                     tag : 'button',
12755                     type : 'button',
12756                     name : 'ok',
12757                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12758                     //html : 'Done'
12759                     html: btn_text_done
12760                 },
12761                 {
12762                     tag : 'button',
12763                     type : 'button',
12764                     name : 'cancel',
12765                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12766                     //html : 'Cancel'
12767                     html: btn_text_cancel
12768                 }
12769             ]
12770         };
12771         
12772         if(this.editable){
12773             buttons.cn.unshift({
12774                 tag: 'input',
12775                 cls: 'roo-select2-search-field-input'
12776             });
12777         }
12778         
12779         var _this = this;
12780         
12781         Roo.each(buttons.cn, function(c){
12782             if (_this.size) {
12783                 c.cls += ' btn-' + _this.size;
12784             }
12785
12786             if (_this.disabled) {
12787                 c.disabled = true;
12788             }
12789         });
12790         
12791         var box = {
12792             tag: 'div',
12793             cn: [
12794                 {
12795                     tag: 'input',
12796                     type : 'hidden',
12797                     cls: 'form-hidden-field'
12798                 },
12799                 {
12800                     tag: 'ul',
12801                     cls: 'roo-select2-choices',
12802                     cn:[
12803                         {
12804                             tag: 'li',
12805                             cls: 'roo-select2-search-field',
12806                             cn: [
12807                                 buttons
12808                             ]
12809                         }
12810                     ]
12811                 }
12812             ]
12813         };
12814         
12815         var combobox = {
12816             cls: 'roo-select2-container input-group roo-select2-container-multi',
12817             cn: [
12818                 box
12819 //                {
12820 //                    tag: 'ul',
12821 //                    cls: 'typeahead typeahead-long dropdown-menu',
12822 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12823 //                }
12824             ]
12825         };
12826         
12827         if(this.hasFeedback && !this.allowBlank){
12828             
12829             var feedback = {
12830                 tag: 'span',
12831                 cls: 'glyphicon form-control-feedback'
12832             };
12833
12834             combobox.cn.push(feedback);
12835         }
12836         
12837         
12838         if (align ==='left' && this.fieldLabel.length) {
12839             
12840             cfg.cls += ' roo-form-group-label-left';
12841             
12842             cfg.cn = [
12843                 {
12844                     tag : 'i',
12845                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12846                     tooltip : 'This field is required'
12847                 },
12848                 {
12849                     tag: 'label',
12850                     'for' :  id,
12851                     cls : 'control-label',
12852                     html : this.fieldLabel
12853
12854                 },
12855                 {
12856                     cls : "", 
12857                     cn: [
12858                         combobox
12859                     ]
12860                 }
12861
12862             ];
12863             
12864             var labelCfg = cfg.cn[1];
12865             var contentCfg = cfg.cn[2];
12866             
12867
12868             if(this.indicatorpos == 'right'){
12869                 
12870                 cfg.cn = [
12871                     {
12872                         tag: 'label',
12873                         'for' :  id,
12874                         cls : 'control-label',
12875                         cn : [
12876                             {
12877                                 tag : 'span',
12878                                 html : this.fieldLabel
12879                             },
12880                             {
12881                                 tag : 'i',
12882                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12883                                 tooltip : 'This field is required'
12884                             }
12885                         ]
12886                     },
12887                     {
12888                         cls : "",
12889                         cn: [
12890                             combobox
12891                         ]
12892                     }
12893
12894                 ];
12895                 
12896                 
12897                 
12898                 labelCfg = cfg.cn[0];
12899                 contentCfg = cfg.cn[1];
12900             
12901             }
12902             
12903             if(this.labelWidth > 12){
12904                 labelCfg.style = "width: " + this.labelWidth + 'px';
12905             }
12906             
12907             if(this.labelWidth < 13 && this.labelmd == 0){
12908                 this.labelmd = this.labelWidth;
12909             }
12910             
12911             if(this.labellg > 0){
12912                 labelCfg.cls += ' col-lg-' + this.labellg;
12913                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12914             }
12915             
12916             if(this.labelmd > 0){
12917                 labelCfg.cls += ' col-md-' + this.labelmd;
12918                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12919             }
12920             
12921             if(this.labelsm > 0){
12922                 labelCfg.cls += ' col-sm-' + this.labelsm;
12923                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12924             }
12925             
12926             if(this.labelxs > 0){
12927                 labelCfg.cls += ' col-xs-' + this.labelxs;
12928                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12929             }
12930                 
12931                 
12932         } else if ( this.fieldLabel.length) {
12933 //                Roo.log(" label");
12934                  cfg.cn = [
12935                     {
12936                         tag : 'i',
12937                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12938                         tooltip : 'This field is required'
12939                     },
12940                     {
12941                         tag: 'label',
12942                         //cls : 'input-group-addon',
12943                         html : this.fieldLabel
12944                     },
12945                     combobox
12946                 ];
12947                 
12948                 if(this.indicatorpos == 'right'){
12949                     cfg.cn = [
12950                         {
12951                             tag: 'label',
12952                             //cls : 'input-group-addon',
12953                             html : this.fieldLabel
12954                         },
12955                         {
12956                             tag : 'i',
12957                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12958                             tooltip : 'This field is required'
12959                         },
12960                         combobox
12961                     ];
12962                     
12963                 }
12964
12965         } else {
12966             
12967 //                Roo.log(" no label && no align");
12968                 cfg = combobox
12969                      
12970                 
12971         }
12972          
12973         var settings=this;
12974         ['xs','sm','md','lg'].map(function(size){
12975             if (settings[size]) {
12976                 cfg.cls += ' col-' + size + '-' + settings[size];
12977             }
12978         });
12979         
12980         return cfg;
12981         
12982     },
12983     
12984     _initEventsCalled : false,
12985     
12986     // private
12987     initEvents: function()
12988     {   
12989         if (this._initEventsCalled) { // as we call render... prevent looping...
12990             return;
12991         }
12992         this._initEventsCalled = true;
12993         
12994         if (!this.store) {
12995             throw "can not find store for combo";
12996         }
12997         
12998         this.store = Roo.factory(this.store, Roo.data);
12999         this.store.parent = this;
13000         
13001         // if we are building from html. then this element is so complex, that we can not really
13002         // use the rendered HTML.
13003         // so we have to trash and replace the previous code.
13004         if (Roo.XComponent.build_from_html) {
13005             // remove this element....
13006             var e = this.el.dom, k=0;
13007             while (e ) { e = e.previousSibling;  ++k;}
13008
13009             this.el.remove();
13010             
13011             this.el=false;
13012             this.rendered = false;
13013             
13014             this.render(this.parent().getChildContainer(true), k);
13015         }
13016         
13017         if(Roo.isIOS && this.useNativeIOS){
13018             this.initIOSView();
13019             return;
13020         }
13021         
13022         /*
13023          * Touch Devices
13024          */
13025         
13026         if(Roo.isTouch && this.mobileTouchView){
13027             this.initTouchView();
13028             return;
13029         }
13030         
13031         if(this.tickable){
13032             this.initTickableEvents();
13033             return;
13034         }
13035         
13036         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13037         
13038         if(this.hiddenName){
13039             
13040             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13041             
13042             this.hiddenField.dom.value =
13043                 this.hiddenValue !== undefined ? this.hiddenValue :
13044                 this.value !== undefined ? this.value : '';
13045
13046             // prevent input submission
13047             this.el.dom.removeAttribute('name');
13048             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13049              
13050              
13051         }
13052         //if(Roo.isGecko){
13053         //    this.el.dom.setAttribute('autocomplete', 'off');
13054         //}
13055         
13056         var cls = 'x-combo-list';
13057         
13058         //this.list = new Roo.Layer({
13059         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13060         //});
13061         
13062         var _this = this;
13063         
13064         (function(){
13065             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13066             _this.list.setWidth(lw);
13067         }).defer(100);
13068         
13069         this.list.on('mouseover', this.onViewOver, this);
13070         this.list.on('mousemove', this.onViewMove, this);
13071         this.list.on('scroll', this.onViewScroll, this);
13072         
13073         /*
13074         this.list.swallowEvent('mousewheel');
13075         this.assetHeight = 0;
13076
13077         if(this.title){
13078             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13079             this.assetHeight += this.header.getHeight();
13080         }
13081
13082         this.innerList = this.list.createChild({cls:cls+'-inner'});
13083         this.innerList.on('mouseover', this.onViewOver, this);
13084         this.innerList.on('mousemove', this.onViewMove, this);
13085         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13086         
13087         if(this.allowBlank && !this.pageSize && !this.disableClear){
13088             this.footer = this.list.createChild({cls:cls+'-ft'});
13089             this.pageTb = new Roo.Toolbar(this.footer);
13090            
13091         }
13092         if(this.pageSize){
13093             this.footer = this.list.createChild({cls:cls+'-ft'});
13094             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13095                     {pageSize: this.pageSize});
13096             
13097         }
13098         
13099         if (this.pageTb && this.allowBlank && !this.disableClear) {
13100             var _this = this;
13101             this.pageTb.add(new Roo.Toolbar.Fill(), {
13102                 cls: 'x-btn-icon x-btn-clear',
13103                 text: '&#160;',
13104                 handler: function()
13105                 {
13106                     _this.collapse();
13107                     _this.clearValue();
13108                     _this.onSelect(false, -1);
13109                 }
13110             });
13111         }
13112         if (this.footer) {
13113             this.assetHeight += this.footer.getHeight();
13114         }
13115         */
13116             
13117         if(!this.tpl){
13118             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13119         }
13120
13121         this.view = new Roo.View(this.list, this.tpl, {
13122             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13123         });
13124         //this.view.wrapEl.setDisplayed(false);
13125         this.view.on('click', this.onViewClick, this);
13126         
13127         
13128         this.store.on('beforeload', this.onBeforeLoad, this);
13129         this.store.on('load', this.onLoad, this);
13130         this.store.on('loadexception', this.onLoadException, this);
13131         /*
13132         if(this.resizable){
13133             this.resizer = new Roo.Resizable(this.list,  {
13134                pinned:true, handles:'se'
13135             });
13136             this.resizer.on('resize', function(r, w, h){
13137                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13138                 this.listWidth = w;
13139                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13140                 this.restrictHeight();
13141             }, this);
13142             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13143         }
13144         */
13145         if(!this.editable){
13146             this.editable = true;
13147             this.setEditable(false);
13148         }
13149         
13150         /*
13151         
13152         if (typeof(this.events.add.listeners) != 'undefined') {
13153             
13154             this.addicon = this.wrap.createChild(
13155                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13156        
13157             this.addicon.on('click', function(e) {
13158                 this.fireEvent('add', this);
13159             }, this);
13160         }
13161         if (typeof(this.events.edit.listeners) != 'undefined') {
13162             
13163             this.editicon = this.wrap.createChild(
13164                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13165             if (this.addicon) {
13166                 this.editicon.setStyle('margin-left', '40px');
13167             }
13168             this.editicon.on('click', function(e) {
13169                 
13170                 // we fire even  if inothing is selected..
13171                 this.fireEvent('edit', this, this.lastData );
13172                 
13173             }, this);
13174         }
13175         */
13176         
13177         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13178             "up" : function(e){
13179                 this.inKeyMode = true;
13180                 this.selectPrev();
13181             },
13182
13183             "down" : function(e){
13184                 if(!this.isExpanded()){
13185                     this.onTriggerClick();
13186                 }else{
13187                     this.inKeyMode = true;
13188                     this.selectNext();
13189                 }
13190             },
13191
13192             "enter" : function(e){
13193 //                this.onViewClick();
13194                 //return true;
13195                 this.collapse();
13196                 
13197                 if(this.fireEvent("specialkey", this, e)){
13198                     this.onViewClick(false);
13199                 }
13200                 
13201                 return true;
13202             },
13203
13204             "esc" : function(e){
13205                 this.collapse();
13206             },
13207
13208             "tab" : function(e){
13209                 this.collapse();
13210                 
13211                 if(this.fireEvent("specialkey", this, e)){
13212                     this.onViewClick(false);
13213                 }
13214                 
13215                 return true;
13216             },
13217
13218             scope : this,
13219
13220             doRelay : function(foo, bar, hname){
13221                 if(hname == 'down' || this.scope.isExpanded()){
13222                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13223                 }
13224                 return true;
13225             },
13226
13227             forceKeyDown: true
13228         });
13229         
13230         
13231         this.queryDelay = Math.max(this.queryDelay || 10,
13232                 this.mode == 'local' ? 10 : 250);
13233         
13234         
13235         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13236         
13237         if(this.typeAhead){
13238             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13239         }
13240         if(this.editable !== false){
13241             this.inputEl().on("keyup", this.onKeyUp, this);
13242         }
13243         if(this.forceSelection){
13244             this.inputEl().on('blur', this.doForce, this);
13245         }
13246         
13247         if(this.multiple){
13248             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13249             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13250         }
13251     },
13252     
13253     initTickableEvents: function()
13254     {   
13255         this.createList();
13256         
13257         if(this.hiddenName){
13258             
13259             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13260             
13261             this.hiddenField.dom.value =
13262                 this.hiddenValue !== undefined ? this.hiddenValue :
13263                 this.value !== undefined ? this.value : '';
13264
13265             // prevent input submission
13266             this.el.dom.removeAttribute('name');
13267             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13268              
13269              
13270         }
13271         
13272 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13273         
13274         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13275         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13276         if(this.triggerList){
13277             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13278         }
13279          
13280         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13281         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13282         
13283         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13284         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13285         
13286         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13287         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13288         
13289         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13290         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13291         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13292         
13293         this.okBtn.hide();
13294         this.cancelBtn.hide();
13295         
13296         var _this = this;
13297         
13298         (function(){
13299             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13300             _this.list.setWidth(lw);
13301         }).defer(100);
13302         
13303         this.list.on('mouseover', this.onViewOver, this);
13304         this.list.on('mousemove', this.onViewMove, this);
13305         
13306         this.list.on('scroll', this.onViewScroll, this);
13307         
13308         if(!this.tpl){
13309             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>';
13310         }
13311
13312         this.view = new Roo.View(this.list, this.tpl, {
13313             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13314         });
13315         
13316         //this.view.wrapEl.setDisplayed(false);
13317         this.view.on('click', this.onViewClick, this);
13318         
13319         
13320         
13321         this.store.on('beforeload', this.onBeforeLoad, this);
13322         this.store.on('load', this.onLoad, this);
13323         this.store.on('loadexception', this.onLoadException, this);
13324         
13325         if(this.editable){
13326             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13327                 "up" : function(e){
13328                     this.inKeyMode = true;
13329                     this.selectPrev();
13330                 },
13331
13332                 "down" : function(e){
13333                     this.inKeyMode = true;
13334                     this.selectNext();
13335                 },
13336
13337                 "enter" : function(e){
13338                     if(this.fireEvent("specialkey", this, e)){
13339                         this.onViewClick(false);
13340                     }
13341                     
13342                     return true;
13343                 },
13344
13345                 "esc" : function(e){
13346                     this.onTickableFooterButtonClick(e, false, false);
13347                 },
13348
13349                 "tab" : function(e){
13350                     this.fireEvent("specialkey", this, e);
13351                     
13352                     this.onTickableFooterButtonClick(e, false, false);
13353                     
13354                     return true;
13355                 },
13356
13357                 scope : this,
13358
13359                 doRelay : function(e, fn, key){
13360                     if(this.scope.isExpanded()){
13361                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13362                     }
13363                     return true;
13364                 },
13365
13366                 forceKeyDown: true
13367             });
13368         }
13369         
13370         this.queryDelay = Math.max(this.queryDelay || 10,
13371                 this.mode == 'local' ? 10 : 250);
13372         
13373         
13374         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13375         
13376         if(this.typeAhead){
13377             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13378         }
13379         
13380         if(this.editable !== false){
13381             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13382         }
13383         
13384         this.indicator = this.indicatorEl();
13385         
13386         if(this.indicator){
13387             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13388             this.indicator.hide();
13389         }
13390         
13391     },
13392
13393     onDestroy : function(){
13394         if(this.view){
13395             this.view.setStore(null);
13396             this.view.el.removeAllListeners();
13397             this.view.el.remove();
13398             this.view.purgeListeners();
13399         }
13400         if(this.list){
13401             this.list.dom.innerHTML  = '';
13402         }
13403         
13404         if(this.store){
13405             this.store.un('beforeload', this.onBeforeLoad, this);
13406             this.store.un('load', this.onLoad, this);
13407             this.store.un('loadexception', this.onLoadException, this);
13408         }
13409         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13410     },
13411
13412     // private
13413     fireKey : function(e){
13414         if(e.isNavKeyPress() && !this.list.isVisible()){
13415             this.fireEvent("specialkey", this, e);
13416         }
13417     },
13418
13419     // private
13420     onResize: function(w, h){
13421 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13422 //        
13423 //        if(typeof w != 'number'){
13424 //            // we do not handle it!?!?
13425 //            return;
13426 //        }
13427 //        var tw = this.trigger.getWidth();
13428 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13429 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13430 //        var x = w - tw;
13431 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13432 //            
13433 //        //this.trigger.setStyle('left', x+'px');
13434 //        
13435 //        if(this.list && this.listWidth === undefined){
13436 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13437 //            this.list.setWidth(lw);
13438 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13439 //        }
13440         
13441     
13442         
13443     },
13444
13445     /**
13446      * Allow or prevent the user from directly editing the field text.  If false is passed,
13447      * the user will only be able to select from the items defined in the dropdown list.  This method
13448      * is the runtime equivalent of setting the 'editable' config option at config time.
13449      * @param {Boolean} value True to allow the user to directly edit the field text
13450      */
13451     setEditable : function(value){
13452         if(value == this.editable){
13453             return;
13454         }
13455         this.editable = value;
13456         if(!value){
13457             this.inputEl().dom.setAttribute('readOnly', true);
13458             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13459             this.inputEl().addClass('x-combo-noedit');
13460         }else{
13461             this.inputEl().dom.setAttribute('readOnly', false);
13462             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13463             this.inputEl().removeClass('x-combo-noedit');
13464         }
13465     },
13466
13467     // private
13468     
13469     onBeforeLoad : function(combo,opts){
13470         if(!this.hasFocus){
13471             return;
13472         }
13473          if (!opts.add) {
13474             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13475          }
13476         this.restrictHeight();
13477         this.selectedIndex = -1;
13478     },
13479
13480     // private
13481     onLoad : function(){
13482         
13483         this.hasQuery = false;
13484         
13485         if(!this.hasFocus){
13486             return;
13487         }
13488         
13489         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13490             this.loading.hide();
13491         }
13492         
13493         if(this.store.getCount() > 0){
13494             
13495             this.expand();
13496             this.restrictHeight();
13497             if(this.lastQuery == this.allQuery){
13498                 if(this.editable && !this.tickable){
13499                     this.inputEl().dom.select();
13500                 }
13501                 
13502                 if(
13503                     !this.selectByValue(this.value, true) &&
13504                     this.autoFocus && 
13505                     (
13506                         !this.store.lastOptions ||
13507                         typeof(this.store.lastOptions.add) == 'undefined' || 
13508                         this.store.lastOptions.add != true
13509                     )
13510                 ){
13511                     this.select(0, true);
13512                 }
13513             }else{
13514                 if(this.autoFocus){
13515                     this.selectNext();
13516                 }
13517                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13518                     this.taTask.delay(this.typeAheadDelay);
13519                 }
13520             }
13521         }else{
13522             this.onEmptyResults();
13523         }
13524         
13525         //this.el.focus();
13526     },
13527     // private
13528     onLoadException : function()
13529     {
13530         this.hasQuery = false;
13531         
13532         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13533             this.loading.hide();
13534         }
13535         
13536         if(this.tickable && this.editable){
13537             return;
13538         }
13539         
13540         this.collapse();
13541         // only causes errors at present
13542         //Roo.log(this.store.reader.jsonData);
13543         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13544             // fixme
13545             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13546         //}
13547         
13548         
13549     },
13550     // private
13551     onTypeAhead : function(){
13552         if(this.store.getCount() > 0){
13553             var r = this.store.getAt(0);
13554             var newValue = r.data[this.displayField];
13555             var len = newValue.length;
13556             var selStart = this.getRawValue().length;
13557             
13558             if(selStart != len){
13559                 this.setRawValue(newValue);
13560                 this.selectText(selStart, newValue.length);
13561             }
13562         }
13563     },
13564
13565     // private
13566     onSelect : function(record, index){
13567         
13568         if(this.fireEvent('beforeselect', this, record, index) !== false){
13569         
13570             this.setFromData(index > -1 ? record.data : false);
13571             
13572             this.collapse();
13573             this.fireEvent('select', this, record, index);
13574         }
13575     },
13576
13577     /**
13578      * Returns the currently selected field value or empty string if no value is set.
13579      * @return {String} value The selected value
13580      */
13581     getValue : function()
13582     {
13583         if(Roo.isIOS && this.useNativeIOS){
13584             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13585         }
13586         
13587         if(this.multiple){
13588             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13589         }
13590         
13591         if(this.valueField){
13592             return typeof this.value != 'undefined' ? this.value : '';
13593         }else{
13594             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13595         }
13596     },
13597     
13598     getRawValue : function()
13599     {
13600         if(Roo.isIOS && this.useNativeIOS){
13601             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13602         }
13603         
13604         var v = this.inputEl().getValue();
13605         
13606         return v;
13607     },
13608
13609     /**
13610      * Clears any text/value currently set in the field
13611      */
13612     clearValue : function(){
13613         
13614         if(this.hiddenField){
13615             this.hiddenField.dom.value = '';
13616         }
13617         this.value = '';
13618         this.setRawValue('');
13619         this.lastSelectionText = '';
13620         this.lastData = false;
13621         
13622         var close = this.closeTriggerEl();
13623         
13624         if(close){
13625             close.hide();
13626         }
13627         
13628         this.validate();
13629         
13630     },
13631
13632     /**
13633      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13634      * will be displayed in the field.  If the value does not match the data value of an existing item,
13635      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13636      * Otherwise the field will be blank (although the value will still be set).
13637      * @param {String} value The value to match
13638      */
13639     setValue : function(v)
13640     {
13641         if(Roo.isIOS && this.useNativeIOS){
13642             this.setIOSValue(v);
13643             return;
13644         }
13645         
13646         if(this.multiple){
13647             this.syncValue();
13648             return;
13649         }
13650         
13651         var text = v;
13652         if(this.valueField){
13653             var r = this.findRecord(this.valueField, v);
13654             if(r){
13655                 text = r.data[this.displayField];
13656             }else if(this.valueNotFoundText !== undefined){
13657                 text = this.valueNotFoundText;
13658             }
13659         }
13660         this.lastSelectionText = text;
13661         if(this.hiddenField){
13662             this.hiddenField.dom.value = v;
13663         }
13664         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13665         this.value = v;
13666         
13667         var close = this.closeTriggerEl();
13668         
13669         if(close){
13670             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13671         }
13672         
13673         this.validate();
13674     },
13675     /**
13676      * @property {Object} the last set data for the element
13677      */
13678     
13679     lastData : false,
13680     /**
13681      * Sets the value of the field based on a object which is related to the record format for the store.
13682      * @param {Object} value the value to set as. or false on reset?
13683      */
13684     setFromData : function(o){
13685         
13686         if(this.multiple){
13687             this.addItem(o);
13688             return;
13689         }
13690             
13691         var dv = ''; // display value
13692         var vv = ''; // value value..
13693         this.lastData = o;
13694         if (this.displayField) {
13695             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13696         } else {
13697             // this is an error condition!!!
13698             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13699         }
13700         
13701         if(this.valueField){
13702             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13703         }
13704         
13705         var close = this.closeTriggerEl();
13706         
13707         if(close){
13708             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13709         }
13710         
13711         if(this.hiddenField){
13712             this.hiddenField.dom.value = vv;
13713             
13714             this.lastSelectionText = dv;
13715             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13716             this.value = vv;
13717             return;
13718         }
13719         // no hidden field.. - we store the value in 'value', but still display
13720         // display field!!!!
13721         this.lastSelectionText = dv;
13722         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13723         this.value = vv;
13724         
13725         
13726         
13727     },
13728     // private
13729     reset : function(){
13730         // overridden so that last data is reset..
13731         
13732         if(this.multiple){
13733             this.clearItem();
13734             return;
13735         }
13736         
13737         this.setValue(this.originalValue);
13738         //this.clearInvalid();
13739         this.lastData = false;
13740         if (this.view) {
13741             this.view.clearSelections();
13742         }
13743         
13744         this.validate();
13745     },
13746     // private
13747     findRecord : function(prop, value){
13748         var record;
13749         if(this.store.getCount() > 0){
13750             this.store.each(function(r){
13751                 if(r.data[prop] == value){
13752                     record = r;
13753                     return false;
13754                 }
13755                 return true;
13756             });
13757         }
13758         return record;
13759     },
13760     
13761     getName: function()
13762     {
13763         // returns hidden if it's set..
13764         if (!this.rendered) {return ''};
13765         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13766         
13767     },
13768     // private
13769     onViewMove : function(e, t){
13770         this.inKeyMode = false;
13771     },
13772
13773     // private
13774     onViewOver : function(e, t){
13775         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13776             return;
13777         }
13778         var item = this.view.findItemFromChild(t);
13779         
13780         if(item){
13781             var index = this.view.indexOf(item);
13782             this.select(index, false);
13783         }
13784     },
13785
13786     // private
13787     onViewClick : function(view, doFocus, el, e)
13788     {
13789         var index = this.view.getSelectedIndexes()[0];
13790         
13791         var r = this.store.getAt(index);
13792         
13793         if(this.tickable){
13794             
13795             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13796                 return;
13797             }
13798             
13799             var rm = false;
13800             var _this = this;
13801             
13802             Roo.each(this.tickItems, function(v,k){
13803                 
13804                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13805                     Roo.log(v);
13806                     _this.tickItems.splice(k, 1);
13807                     
13808                     if(typeof(e) == 'undefined' && view == false){
13809                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13810                     }
13811                     
13812                     rm = true;
13813                     return;
13814                 }
13815             });
13816             
13817             if(rm){
13818                 return;
13819             }
13820             
13821             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13822                 this.tickItems.push(r.data);
13823             }
13824             
13825             if(typeof(e) == 'undefined' && view == false){
13826                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13827             }
13828                     
13829             return;
13830         }
13831         
13832         if(r){
13833             this.onSelect(r, index);
13834         }
13835         if(doFocus !== false && !this.blockFocus){
13836             this.inputEl().focus();
13837         }
13838     },
13839
13840     // private
13841     restrictHeight : function(){
13842         //this.innerList.dom.style.height = '';
13843         //var inner = this.innerList.dom;
13844         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13845         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13846         //this.list.beginUpdate();
13847         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13848         this.list.alignTo(this.inputEl(), this.listAlign);
13849         this.list.alignTo(this.inputEl(), this.listAlign);
13850         //this.list.endUpdate();
13851     },
13852
13853     // private
13854     onEmptyResults : function(){
13855         
13856         if(this.tickable && this.editable){
13857             this.restrictHeight();
13858             return;
13859         }
13860         
13861         this.collapse();
13862     },
13863
13864     /**
13865      * Returns true if the dropdown list is expanded, else false.
13866      */
13867     isExpanded : function(){
13868         return this.list.isVisible();
13869     },
13870
13871     /**
13872      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13873      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13874      * @param {String} value The data value of the item to select
13875      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13876      * selected item if it is not currently in view (defaults to true)
13877      * @return {Boolean} True if the value matched an item in the list, else false
13878      */
13879     selectByValue : function(v, scrollIntoView){
13880         if(v !== undefined && v !== null){
13881             var r = this.findRecord(this.valueField || this.displayField, v);
13882             if(r){
13883                 this.select(this.store.indexOf(r), scrollIntoView);
13884                 return true;
13885             }
13886         }
13887         return false;
13888     },
13889
13890     /**
13891      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13892      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13893      * @param {Number} index The zero-based index of the list item to select
13894      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13895      * selected item if it is not currently in view (defaults to true)
13896      */
13897     select : function(index, scrollIntoView){
13898         this.selectedIndex = index;
13899         this.view.select(index);
13900         if(scrollIntoView !== false){
13901             var el = this.view.getNode(index);
13902             /*
13903              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13904              */
13905             if(el){
13906                 this.list.scrollChildIntoView(el, false);
13907             }
13908         }
13909     },
13910
13911     // private
13912     selectNext : function(){
13913         var ct = this.store.getCount();
13914         if(ct > 0){
13915             if(this.selectedIndex == -1){
13916                 this.select(0);
13917             }else if(this.selectedIndex < ct-1){
13918                 this.select(this.selectedIndex+1);
13919             }
13920         }
13921     },
13922
13923     // private
13924     selectPrev : function(){
13925         var ct = this.store.getCount();
13926         if(ct > 0){
13927             if(this.selectedIndex == -1){
13928                 this.select(0);
13929             }else if(this.selectedIndex != 0){
13930                 this.select(this.selectedIndex-1);
13931             }
13932         }
13933     },
13934
13935     // private
13936     onKeyUp : function(e){
13937         if(this.editable !== false && !e.isSpecialKey()){
13938             this.lastKey = e.getKey();
13939             this.dqTask.delay(this.queryDelay);
13940         }
13941     },
13942
13943     // private
13944     validateBlur : function(){
13945         return !this.list || !this.list.isVisible();   
13946     },
13947
13948     // private
13949     initQuery : function(){
13950         
13951         var v = this.getRawValue();
13952         
13953         if(this.tickable && this.editable){
13954             v = this.tickableInputEl().getValue();
13955         }
13956         
13957         this.doQuery(v);
13958     },
13959
13960     // private
13961     doForce : function(){
13962         if(this.inputEl().dom.value.length > 0){
13963             this.inputEl().dom.value =
13964                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13965              
13966         }
13967     },
13968
13969     /**
13970      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13971      * query allowing the query action to be canceled if needed.
13972      * @param {String} query The SQL query to execute
13973      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13974      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13975      * saved in the current store (defaults to false)
13976      */
13977     doQuery : function(q, forceAll){
13978         
13979         if(q === undefined || q === null){
13980             q = '';
13981         }
13982         var qe = {
13983             query: q,
13984             forceAll: forceAll,
13985             combo: this,
13986             cancel:false
13987         };
13988         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13989             return false;
13990         }
13991         q = qe.query;
13992         
13993         forceAll = qe.forceAll;
13994         if(forceAll === true || (q.length >= this.minChars)){
13995             
13996             this.hasQuery = true;
13997             
13998             if(this.lastQuery != q || this.alwaysQuery){
13999                 this.lastQuery = q;
14000                 if(this.mode == 'local'){
14001                     this.selectedIndex = -1;
14002                     if(forceAll){
14003                         this.store.clearFilter();
14004                     }else{
14005                         
14006                         if(this.specialFilter){
14007                             this.fireEvent('specialfilter', this);
14008                             this.onLoad();
14009                             return;
14010                         }
14011                         
14012                         this.store.filter(this.displayField, q);
14013                     }
14014                     
14015                     this.store.fireEvent("datachanged", this.store);
14016                     
14017                     this.onLoad();
14018                     
14019                     
14020                 }else{
14021                     
14022                     this.store.baseParams[this.queryParam] = q;
14023                     
14024                     var options = {params : this.getParams(q)};
14025                     
14026                     if(this.loadNext){
14027                         options.add = true;
14028                         options.params.start = this.page * this.pageSize;
14029                     }
14030                     
14031                     this.store.load(options);
14032                     
14033                     /*
14034                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14035                      *  we should expand the list on onLoad
14036                      *  so command out it
14037                      */
14038 //                    this.expand();
14039                 }
14040             }else{
14041                 this.selectedIndex = -1;
14042                 this.onLoad();   
14043             }
14044         }
14045         
14046         this.loadNext = false;
14047     },
14048     
14049     // private
14050     getParams : function(q){
14051         var p = {};
14052         //p[this.queryParam] = q;
14053         
14054         if(this.pageSize){
14055             p.start = 0;
14056             p.limit = this.pageSize;
14057         }
14058         return p;
14059     },
14060
14061     /**
14062      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14063      */
14064     collapse : function(){
14065         if(!this.isExpanded()){
14066             return;
14067         }
14068         
14069         this.list.hide();
14070         
14071         this.hasFocus = false;
14072         
14073         if(this.tickable){
14074             this.okBtn.hide();
14075             this.cancelBtn.hide();
14076             this.trigger.show();
14077             
14078             if(this.editable){
14079                 this.tickableInputEl().dom.value = '';
14080                 this.tickableInputEl().blur();
14081             }
14082             
14083         }
14084         
14085         Roo.get(document).un('mousedown', this.collapseIf, this);
14086         Roo.get(document).un('mousewheel', this.collapseIf, this);
14087         if (!this.editable) {
14088             Roo.get(document).un('keydown', this.listKeyPress, this);
14089         }
14090         this.fireEvent('collapse', this);
14091         
14092         this.validate();
14093     },
14094
14095     // private
14096     collapseIf : function(e){
14097         var in_combo  = e.within(this.el);
14098         var in_list =  e.within(this.list);
14099         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14100         
14101         if (in_combo || in_list || is_list) {
14102             //e.stopPropagation();
14103             return;
14104         }
14105         
14106         if(this.tickable){
14107             this.onTickableFooterButtonClick(e, false, false);
14108         }
14109
14110         this.collapse();
14111         
14112     },
14113
14114     /**
14115      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14116      */
14117     expand : function(){
14118        
14119         if(this.isExpanded() || !this.hasFocus){
14120             return;
14121         }
14122         
14123         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14124         this.list.setWidth(lw);
14125         
14126         Roo.log('expand');
14127         
14128         this.list.show();
14129         
14130         this.restrictHeight();
14131         
14132         if(this.tickable){
14133             
14134             this.tickItems = Roo.apply([], this.item);
14135             
14136             this.okBtn.show();
14137             this.cancelBtn.show();
14138             this.trigger.hide();
14139             
14140             if(this.editable){
14141                 this.tickableInputEl().focus();
14142             }
14143             
14144         }
14145         
14146         Roo.get(document).on('mousedown', this.collapseIf, this);
14147         Roo.get(document).on('mousewheel', this.collapseIf, this);
14148         if (!this.editable) {
14149             Roo.get(document).on('keydown', this.listKeyPress, this);
14150         }
14151         
14152         this.fireEvent('expand', this);
14153     },
14154
14155     // private
14156     // Implements the default empty TriggerField.onTriggerClick function
14157     onTriggerClick : function(e)
14158     {
14159         Roo.log('trigger click');
14160         
14161         if(this.disabled || !this.triggerList){
14162             return;
14163         }
14164         
14165         this.page = 0;
14166         this.loadNext = false;
14167         
14168         if(this.isExpanded()){
14169             this.collapse();
14170             if (!this.blockFocus) {
14171                 this.inputEl().focus();
14172             }
14173             
14174         }else {
14175             this.hasFocus = true;
14176             if(this.triggerAction == 'all') {
14177                 this.doQuery(this.allQuery, true);
14178             } else {
14179                 this.doQuery(this.getRawValue());
14180             }
14181             if (!this.blockFocus) {
14182                 this.inputEl().focus();
14183             }
14184         }
14185     },
14186     
14187     onTickableTriggerClick : function(e)
14188     {
14189         if(this.disabled){
14190             return;
14191         }
14192         
14193         this.page = 0;
14194         this.loadNext = false;
14195         this.hasFocus = true;
14196         
14197         if(this.triggerAction == 'all') {
14198             this.doQuery(this.allQuery, true);
14199         } else {
14200             this.doQuery(this.getRawValue());
14201         }
14202     },
14203     
14204     onSearchFieldClick : function(e)
14205     {
14206         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14207             this.onTickableFooterButtonClick(e, false, false);
14208             return;
14209         }
14210         
14211         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14212             return;
14213         }
14214         
14215         this.page = 0;
14216         this.loadNext = false;
14217         this.hasFocus = true;
14218         
14219         if(this.triggerAction == 'all') {
14220             this.doQuery(this.allQuery, true);
14221         } else {
14222             this.doQuery(this.getRawValue());
14223         }
14224     },
14225     
14226     listKeyPress : function(e)
14227     {
14228         //Roo.log('listkeypress');
14229         // scroll to first matching element based on key pres..
14230         if (e.isSpecialKey()) {
14231             return false;
14232         }
14233         var k = String.fromCharCode(e.getKey()).toUpperCase();
14234         //Roo.log(k);
14235         var match  = false;
14236         var csel = this.view.getSelectedNodes();
14237         var cselitem = false;
14238         if (csel.length) {
14239             var ix = this.view.indexOf(csel[0]);
14240             cselitem  = this.store.getAt(ix);
14241             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14242                 cselitem = false;
14243             }
14244             
14245         }
14246         
14247         this.store.each(function(v) { 
14248             if (cselitem) {
14249                 // start at existing selection.
14250                 if (cselitem.id == v.id) {
14251                     cselitem = false;
14252                 }
14253                 return true;
14254             }
14255                 
14256             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14257                 match = this.store.indexOf(v);
14258                 return false;
14259             }
14260             return true;
14261         }, this);
14262         
14263         if (match === false) {
14264             return true; // no more action?
14265         }
14266         // scroll to?
14267         this.view.select(match);
14268         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14269         sn.scrollIntoView(sn.dom.parentNode, false);
14270     },
14271     
14272     onViewScroll : function(e, t){
14273         
14274         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){
14275             return;
14276         }
14277         
14278         this.hasQuery = true;
14279         
14280         this.loading = this.list.select('.loading', true).first();
14281         
14282         if(this.loading === null){
14283             this.list.createChild({
14284                 tag: 'div',
14285                 cls: 'loading roo-select2-more-results roo-select2-active',
14286                 html: 'Loading more results...'
14287             });
14288             
14289             this.loading = this.list.select('.loading', true).first();
14290             
14291             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14292             
14293             this.loading.hide();
14294         }
14295         
14296         this.loading.show();
14297         
14298         var _combo = this;
14299         
14300         this.page++;
14301         this.loadNext = true;
14302         
14303         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14304         
14305         return;
14306     },
14307     
14308     addItem : function(o)
14309     {   
14310         var dv = ''; // display value
14311         
14312         if (this.displayField) {
14313             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14314         } else {
14315             // this is an error condition!!!
14316             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14317         }
14318         
14319         if(!dv.length){
14320             return;
14321         }
14322         
14323         var choice = this.choices.createChild({
14324             tag: 'li',
14325             cls: 'roo-select2-search-choice',
14326             cn: [
14327                 {
14328                     tag: 'div',
14329                     html: dv
14330                 },
14331                 {
14332                     tag: 'a',
14333                     href: '#',
14334                     cls: 'roo-select2-search-choice-close fa fa-times',
14335                     tabindex: '-1'
14336                 }
14337             ]
14338             
14339         }, this.searchField);
14340         
14341         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14342         
14343         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14344         
14345         this.item.push(o);
14346         
14347         this.lastData = o;
14348         
14349         this.syncValue();
14350         
14351         this.inputEl().dom.value = '';
14352         
14353         this.validate();
14354     },
14355     
14356     onRemoveItem : function(e, _self, o)
14357     {
14358         e.preventDefault();
14359         
14360         this.lastItem = Roo.apply([], this.item);
14361         
14362         var index = this.item.indexOf(o.data) * 1;
14363         
14364         if( index < 0){
14365             Roo.log('not this item?!');
14366             return;
14367         }
14368         
14369         this.item.splice(index, 1);
14370         o.item.remove();
14371         
14372         this.syncValue();
14373         
14374         this.fireEvent('remove', this, e);
14375         
14376         this.validate();
14377         
14378     },
14379     
14380     syncValue : function()
14381     {
14382         if(!this.item.length){
14383             this.clearValue();
14384             return;
14385         }
14386             
14387         var value = [];
14388         var _this = this;
14389         Roo.each(this.item, function(i){
14390             if(_this.valueField){
14391                 value.push(i[_this.valueField]);
14392                 return;
14393             }
14394
14395             value.push(i);
14396         });
14397
14398         this.value = value.join(',');
14399
14400         if(this.hiddenField){
14401             this.hiddenField.dom.value = this.value;
14402         }
14403         
14404         this.store.fireEvent("datachanged", this.store);
14405         
14406         this.validate();
14407     },
14408     
14409     clearItem : function()
14410     {
14411         if(!this.multiple){
14412             return;
14413         }
14414         
14415         this.item = [];
14416         
14417         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14418            c.remove();
14419         });
14420         
14421         this.syncValue();
14422         
14423         this.validate();
14424         
14425         if(this.tickable && !Roo.isTouch){
14426             this.view.refresh();
14427         }
14428     },
14429     
14430     inputEl: function ()
14431     {
14432         if(Roo.isIOS && this.useNativeIOS){
14433             return this.el.select('select.roo-ios-select', true).first();
14434         }
14435         
14436         if(Roo.isTouch && this.mobileTouchView){
14437             return this.el.select('input.form-control',true).first();
14438         }
14439         
14440         if(this.tickable){
14441             return this.searchField;
14442         }
14443         
14444         return this.el.select('input.form-control',true).first();
14445     },
14446     
14447     onTickableFooterButtonClick : function(e, btn, el)
14448     {
14449         e.preventDefault();
14450         
14451         this.lastItem = Roo.apply([], this.item);
14452         
14453         if(btn && btn.name == 'cancel'){
14454             this.tickItems = Roo.apply([], this.item);
14455             this.collapse();
14456             return;
14457         }
14458         
14459         this.clearItem();
14460         
14461         var _this = this;
14462         
14463         Roo.each(this.tickItems, function(o){
14464             _this.addItem(o);
14465         });
14466         
14467         this.collapse();
14468         
14469     },
14470     
14471     validate : function()
14472     {
14473         var v = this.getRawValue();
14474         
14475         if(this.multiple){
14476             v = this.getValue();
14477         }
14478         
14479         if(this.disabled || this.allowBlank || v.length){
14480             this.markValid();
14481             return true;
14482         }
14483         
14484         this.markInvalid();
14485         return false;
14486     },
14487     
14488     tickableInputEl : function()
14489     {
14490         if(!this.tickable || !this.editable){
14491             return this.inputEl();
14492         }
14493         
14494         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14495     },
14496     
14497     
14498     getAutoCreateTouchView : function()
14499     {
14500         var id = Roo.id();
14501         
14502         var cfg = {
14503             cls: 'form-group' //input-group
14504         };
14505         
14506         var input =  {
14507             tag: 'input',
14508             id : id,
14509             type : this.inputType,
14510             cls : 'form-control x-combo-noedit',
14511             autocomplete: 'new-password',
14512             placeholder : this.placeholder || '',
14513             readonly : true
14514         };
14515         
14516         if (this.name) {
14517             input.name = this.name;
14518         }
14519         
14520         if (this.size) {
14521             input.cls += ' input-' + this.size;
14522         }
14523         
14524         if (this.disabled) {
14525             input.disabled = true;
14526         }
14527         
14528         var inputblock = {
14529             cls : '',
14530             cn : [
14531                 input
14532             ]
14533         };
14534         
14535         if(this.before){
14536             inputblock.cls += ' input-group';
14537             
14538             inputblock.cn.unshift({
14539                 tag :'span',
14540                 cls : 'input-group-addon',
14541                 html : this.before
14542             });
14543         }
14544         
14545         if(this.removable && !this.multiple){
14546             inputblock.cls += ' roo-removable';
14547             
14548             inputblock.cn.push({
14549                 tag: 'button',
14550                 html : 'x',
14551                 cls : 'roo-combo-removable-btn close'
14552             });
14553         }
14554
14555         if(this.hasFeedback && !this.allowBlank){
14556             
14557             inputblock.cls += ' has-feedback';
14558             
14559             inputblock.cn.push({
14560                 tag: 'span',
14561                 cls: 'glyphicon form-control-feedback'
14562             });
14563             
14564         }
14565         
14566         if (this.after) {
14567             
14568             inputblock.cls += (this.before) ? '' : ' input-group';
14569             
14570             inputblock.cn.push({
14571                 tag :'span',
14572                 cls : 'input-group-addon',
14573                 html : this.after
14574             });
14575         }
14576
14577         var box = {
14578             tag: 'div',
14579             cn: [
14580                 {
14581                     tag: 'input',
14582                     type : 'hidden',
14583                     cls: 'form-hidden-field'
14584                 },
14585                 inputblock
14586             ]
14587             
14588         };
14589         
14590         if(this.multiple){
14591             box = {
14592                 tag: 'div',
14593                 cn: [
14594                     {
14595                         tag: 'input',
14596                         type : 'hidden',
14597                         cls: 'form-hidden-field'
14598                     },
14599                     {
14600                         tag: 'ul',
14601                         cls: 'roo-select2-choices',
14602                         cn:[
14603                             {
14604                                 tag: 'li',
14605                                 cls: 'roo-select2-search-field',
14606                                 cn: [
14607
14608                                     inputblock
14609                                 ]
14610                             }
14611                         ]
14612                     }
14613                 ]
14614             }
14615         };
14616         
14617         var combobox = {
14618             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14619             cn: [
14620                 box
14621             ]
14622         };
14623         
14624         if(!this.multiple && this.showToggleBtn){
14625             
14626             var caret = {
14627                         tag: 'span',
14628                         cls: 'caret'
14629             };
14630             
14631             if (this.caret != false) {
14632                 caret = {
14633                      tag: 'i',
14634                      cls: 'fa fa-' + this.caret
14635                 };
14636                 
14637             }
14638             
14639             combobox.cn.push({
14640                 tag :'span',
14641                 cls : 'input-group-addon btn dropdown-toggle',
14642                 cn : [
14643                     caret,
14644                     {
14645                         tag: 'span',
14646                         cls: 'combobox-clear',
14647                         cn  : [
14648                             {
14649                                 tag : 'i',
14650                                 cls: 'icon-remove'
14651                             }
14652                         ]
14653                     }
14654                 ]
14655
14656             })
14657         }
14658         
14659         if(this.multiple){
14660             combobox.cls += ' roo-select2-container-multi';
14661         }
14662         
14663         var align = this.labelAlign || this.parentLabelAlign();
14664         
14665         if (align ==='left' && this.fieldLabel.length) {
14666
14667             cfg.cn = [
14668                 {
14669                    tag : 'i',
14670                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14671                    tooltip : 'This field is required'
14672                 },
14673                 {
14674                     tag: 'label',
14675                     cls : 'control-label',
14676                     html : this.fieldLabel
14677
14678                 },
14679                 {
14680                     cls : '', 
14681                     cn: [
14682                         combobox
14683                     ]
14684                 }
14685             ];
14686             
14687             var labelCfg = cfg.cn[1];
14688             var contentCfg = cfg.cn[2];
14689             
14690
14691             if(this.indicatorpos == 'right'){
14692                 cfg.cn = [
14693                     {
14694                         tag: 'label',
14695                         cls : 'control-label',
14696                         html : this.fieldLabel,
14697                         cn : [
14698                             {
14699                                tag : 'i',
14700                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14701                                tooltip : 'This field is required'
14702                             }
14703                         ]
14704                     },
14705                     {
14706                         cls : '', 
14707                         cn: [
14708                             combobox
14709                         ]
14710                     }
14711                 ];
14712             }
14713             
14714             labelCfg = cfg.cn[0];
14715             contentCfg = cfg.cn[2];
14716             
14717             if(this.labelWidth > 12){
14718                 labelCfg.style = "width: " + this.labelWidth + 'px';
14719             }
14720             
14721             if(this.labelWidth < 13 && this.labelmd == 0){
14722                 this.labelmd = this.labelWidth;
14723             }
14724             
14725             if(this.labellg > 0){
14726                 labelCfg.cls += ' col-lg-' + this.labellg;
14727                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14728             }
14729             
14730             if(this.labelmd > 0){
14731                 labelCfg.cls += ' col-md-' + this.labelmd;
14732                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14733             }
14734             
14735             if(this.labelsm > 0){
14736                 labelCfg.cls += ' col-sm-' + this.labelsm;
14737                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14738             }
14739             
14740             if(this.labelxs > 0){
14741                 labelCfg.cls += ' col-xs-' + this.labelxs;
14742                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14743             }
14744                 
14745                 
14746         } else if ( this.fieldLabel.length) {
14747             cfg.cn = [
14748                 {
14749                    tag : 'i',
14750                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14751                    tooltip : 'This field is required'
14752                 },
14753                 {
14754                     tag: 'label',
14755                     cls : 'control-label',
14756                     html : this.fieldLabel
14757
14758                 },
14759                 {
14760                     cls : '', 
14761                     cn: [
14762                         combobox
14763                     ]
14764                 }
14765             ];
14766             
14767             if(this.indicatorpos == 'right'){
14768                 cfg.cn = [
14769                     {
14770                         tag: 'label',
14771                         cls : 'control-label',
14772                         html : this.fieldLabel,
14773                         cn : [
14774                             {
14775                                tag : 'i',
14776                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14777                                tooltip : 'This field is required'
14778                             }
14779                         ]
14780                     },
14781                     {
14782                         cls : '', 
14783                         cn: [
14784                             combobox
14785                         ]
14786                     }
14787                 ];
14788             }
14789         } else {
14790             cfg.cn = combobox;    
14791         }
14792         
14793         
14794         var settings = this;
14795         
14796         ['xs','sm','md','lg'].map(function(size){
14797             if (settings[size]) {
14798                 cfg.cls += ' col-' + size + '-' + settings[size];
14799             }
14800         });
14801         
14802         return cfg;
14803     },
14804     
14805     initTouchView : function()
14806     {
14807         this.renderTouchView();
14808         
14809         this.touchViewEl.on('scroll', function(){
14810             this.el.dom.scrollTop = 0;
14811         }, this);
14812         
14813         this.originalValue = this.getValue();
14814         
14815         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14816         
14817         this.inputEl().on("click", this.showTouchView, this);
14818         if (this.triggerEl) {
14819             this.triggerEl.on("click", this.showTouchView, this);
14820         }
14821         
14822         
14823         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14824         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14825         
14826         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14827         
14828         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14829         this.store.on('load', this.onTouchViewLoad, this);
14830         this.store.on('loadexception', this.onTouchViewLoadException, this);
14831         
14832         if(this.hiddenName){
14833             
14834             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14835             
14836             this.hiddenField.dom.value =
14837                 this.hiddenValue !== undefined ? this.hiddenValue :
14838                 this.value !== undefined ? this.value : '';
14839         
14840             this.el.dom.removeAttribute('name');
14841             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14842         }
14843         
14844         if(this.multiple){
14845             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14846             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14847         }
14848         
14849         if(this.removable && !this.multiple){
14850             var close = this.closeTriggerEl();
14851             if(close){
14852                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14853                 close.on('click', this.removeBtnClick, this, close);
14854             }
14855         }
14856         /*
14857          * fix the bug in Safari iOS8
14858          */
14859         this.inputEl().on("focus", function(e){
14860             document.activeElement.blur();
14861         }, this);
14862         
14863         return;
14864         
14865         
14866     },
14867     
14868     renderTouchView : function()
14869     {
14870         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14871         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14872         
14873         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14874         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14875         
14876         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14877         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14878         this.touchViewBodyEl.setStyle('overflow', 'auto');
14879         
14880         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14881         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14882         
14883         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14884         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14885         
14886     },
14887     
14888     showTouchView : function()
14889     {
14890         if(this.disabled){
14891             return;
14892         }
14893         
14894         this.touchViewHeaderEl.hide();
14895
14896         if(this.modalTitle.length){
14897             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14898             this.touchViewHeaderEl.show();
14899         }
14900
14901         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14902         this.touchViewEl.show();
14903
14904         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14905         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14906                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14907
14908         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14909
14910         if(this.modalTitle.length){
14911             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14912         }
14913         
14914         this.touchViewBodyEl.setHeight(bodyHeight);
14915
14916         if(this.animate){
14917             var _this = this;
14918             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14919         }else{
14920             this.touchViewEl.addClass('in');
14921         }
14922
14923         this.doTouchViewQuery();
14924         
14925     },
14926     
14927     hideTouchView : function()
14928     {
14929         this.touchViewEl.removeClass('in');
14930
14931         if(this.animate){
14932             var _this = this;
14933             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14934         }else{
14935             this.touchViewEl.setStyle('display', 'none');
14936         }
14937         
14938     },
14939     
14940     setTouchViewValue : function()
14941     {
14942         if(this.multiple){
14943             this.clearItem();
14944         
14945             var _this = this;
14946
14947             Roo.each(this.tickItems, function(o){
14948                 this.addItem(o);
14949             }, this);
14950         }
14951         
14952         this.hideTouchView();
14953     },
14954     
14955     doTouchViewQuery : function()
14956     {
14957         var qe = {
14958             query: '',
14959             forceAll: true,
14960             combo: this,
14961             cancel:false
14962         };
14963         
14964         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14965             return false;
14966         }
14967         
14968         if(!this.alwaysQuery || this.mode == 'local'){
14969             this.onTouchViewLoad();
14970             return;
14971         }
14972         
14973         this.store.load();
14974     },
14975     
14976     onTouchViewBeforeLoad : function(combo,opts)
14977     {
14978         return;
14979     },
14980
14981     // private
14982     onTouchViewLoad : function()
14983     {
14984         if(this.store.getCount() < 1){
14985             this.onTouchViewEmptyResults();
14986             return;
14987         }
14988         
14989         this.clearTouchView();
14990         
14991         var rawValue = this.getRawValue();
14992         
14993         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14994         
14995         this.tickItems = [];
14996         
14997         this.store.data.each(function(d, rowIndex){
14998             var row = this.touchViewListGroup.createChild(template);
14999             
15000             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15001                 row.addClass(d.data.cls);
15002             }
15003             
15004             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15005                 var cfg = {
15006                     data : d.data,
15007                     html : d.data[this.displayField]
15008                 };
15009                 
15010                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15011                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15012                 }
15013             }
15014             row.removeClass('selected');
15015             if(!this.multiple && this.valueField &&
15016                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15017             {
15018                 // radio buttons..
15019                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15020                 row.addClass('selected');
15021             }
15022             
15023             if(this.multiple && this.valueField &&
15024                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15025             {
15026                 
15027                 // checkboxes...
15028                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15029                 this.tickItems.push(d.data);
15030             }
15031             
15032             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15033             
15034         }, this);
15035         
15036         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15037         
15038         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15039
15040         if(this.modalTitle.length){
15041             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15042         }
15043
15044         var listHeight = this.touchViewListGroup.getHeight();
15045         
15046         var _this = this;
15047         
15048         if(firstChecked && listHeight > bodyHeight){
15049             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15050         }
15051         
15052     },
15053     
15054     onTouchViewLoadException : function()
15055     {
15056         this.hideTouchView();
15057     },
15058     
15059     onTouchViewEmptyResults : function()
15060     {
15061         this.clearTouchView();
15062         
15063         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15064         
15065         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15066         
15067     },
15068     
15069     clearTouchView : function()
15070     {
15071         this.touchViewListGroup.dom.innerHTML = '';
15072     },
15073     
15074     onTouchViewClick : function(e, el, o)
15075     {
15076         e.preventDefault();
15077         
15078         var row = o.row;
15079         var rowIndex = o.rowIndex;
15080         
15081         var r = this.store.getAt(rowIndex);
15082         
15083         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15084             
15085             if(!this.multiple){
15086                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15087                     c.dom.removeAttribute('checked');
15088                 }, this);
15089
15090                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15091
15092                 this.setFromData(r.data);
15093
15094                 var close = this.closeTriggerEl();
15095
15096                 if(close){
15097                     close.show();
15098                 }
15099
15100                 this.hideTouchView();
15101
15102                 this.fireEvent('select', this, r, rowIndex);
15103
15104                 return;
15105             }
15106
15107             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15108                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15109                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15110                 return;
15111             }
15112
15113             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15114             this.addItem(r.data);
15115             this.tickItems.push(r.data);
15116         }
15117     },
15118     
15119     getAutoCreateNativeIOS : function()
15120     {
15121         var cfg = {
15122             cls: 'form-group' //input-group,
15123         };
15124         
15125         var combobox =  {
15126             tag: 'select',
15127             cls : 'roo-ios-select'
15128         };
15129         
15130         if (this.name) {
15131             combobox.name = this.name;
15132         }
15133         
15134         if (this.disabled) {
15135             combobox.disabled = true;
15136         }
15137         
15138         var settings = this;
15139         
15140         ['xs','sm','md','lg'].map(function(size){
15141             if (settings[size]) {
15142                 cfg.cls += ' col-' + size + '-' + settings[size];
15143             }
15144         });
15145         
15146         cfg.cn = combobox;
15147         
15148         return cfg;
15149         
15150     },
15151     
15152     initIOSView : function()
15153     {
15154         this.store.on('load', this.onIOSViewLoad, this);
15155         
15156         return;
15157     },
15158     
15159     onIOSViewLoad : function()
15160     {
15161         if(this.store.getCount() < 1){
15162             return;
15163         }
15164         
15165         this.clearIOSView();
15166         
15167         if(this.allowBlank) {
15168             
15169             var default_text = '-- SELECT --';
15170             
15171             var opt = this.inputEl().createChild({
15172                 tag: 'option',
15173                 value : 0,
15174                 html : default_text
15175             });
15176             
15177             var o = {};
15178             o[this.valueField] = 0;
15179             o[this.displayField] = default_text;
15180             
15181             this.ios_options.push({
15182                 data : o,
15183                 el : opt
15184             });
15185             
15186         }
15187         
15188         this.store.data.each(function(d, rowIndex){
15189             
15190             var html = '';
15191             
15192             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15193                 html = d.data[this.displayField];
15194             }
15195             
15196             var value = '';
15197             
15198             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15199                 value = d.data[this.valueField];
15200             }
15201             
15202             var option = {
15203                 tag: 'option',
15204                 value : value,
15205                 html : html
15206             };
15207             
15208             if(this.value == d.data[this.valueField]){
15209                 option['selected'] = true;
15210             }
15211             
15212             var opt = this.inputEl().createChild(option);
15213             
15214             this.ios_options.push({
15215                 data : d.data,
15216                 el : opt
15217             });
15218             
15219         }, this);
15220         
15221         this.inputEl().on('change', function(){
15222            this.fireEvent('select', this);
15223         }, this);
15224         
15225     },
15226     
15227     clearIOSView: function()
15228     {
15229         this.inputEl().dom.innerHTML = '';
15230         
15231         this.ios_options = [];
15232     },
15233     
15234     setIOSValue: function(v)
15235     {
15236         this.value = v;
15237         
15238         if(!this.ios_options){
15239             return;
15240         }
15241         
15242         Roo.each(this.ios_options, function(opts){
15243            
15244            opts.el.dom.removeAttribute('selected');
15245            
15246            if(opts.data[this.valueField] != v){
15247                return;
15248            }
15249            
15250            opts.el.dom.setAttribute('selected', true);
15251            
15252         }, this);
15253     }
15254
15255     /** 
15256     * @cfg {Boolean} grow 
15257     * @hide 
15258     */
15259     /** 
15260     * @cfg {Number} growMin 
15261     * @hide 
15262     */
15263     /** 
15264     * @cfg {Number} growMax 
15265     * @hide 
15266     */
15267     /**
15268      * @hide
15269      * @method autoSize
15270      */
15271 });
15272
15273 Roo.apply(Roo.bootstrap.ComboBox,  {
15274     
15275     header : {
15276         tag: 'div',
15277         cls: 'modal-header',
15278         cn: [
15279             {
15280                 tag: 'h4',
15281                 cls: 'modal-title'
15282             }
15283         ]
15284     },
15285     
15286     body : {
15287         tag: 'div',
15288         cls: 'modal-body',
15289         cn: [
15290             {
15291                 tag: 'ul',
15292                 cls: 'list-group'
15293             }
15294         ]
15295     },
15296     
15297     listItemRadio : {
15298         tag: 'li',
15299         cls: 'list-group-item',
15300         cn: [
15301             {
15302                 tag: 'span',
15303                 cls: 'roo-combobox-list-group-item-value'
15304             },
15305             {
15306                 tag: 'div',
15307                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15308                 cn: [
15309                     {
15310                         tag: 'input',
15311                         type: 'radio'
15312                     },
15313                     {
15314                         tag: 'label'
15315                     }
15316                 ]
15317             }
15318         ]
15319     },
15320     
15321     listItemCheckbox : {
15322         tag: 'li',
15323         cls: 'list-group-item',
15324         cn: [
15325             {
15326                 tag: 'span',
15327                 cls: 'roo-combobox-list-group-item-value'
15328             },
15329             {
15330                 tag: 'div',
15331                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15332                 cn: [
15333                     {
15334                         tag: 'input',
15335                         type: 'checkbox'
15336                     },
15337                     {
15338                         tag: 'label'
15339                     }
15340                 ]
15341             }
15342         ]
15343     },
15344     
15345     emptyResult : {
15346         tag: 'div',
15347         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15348     },
15349     
15350     footer : {
15351         tag: 'div',
15352         cls: 'modal-footer',
15353         cn: [
15354             {
15355                 tag: 'div',
15356                 cls: 'row',
15357                 cn: [
15358                     {
15359                         tag: 'div',
15360                         cls: 'col-xs-6 text-left',
15361                         cn: {
15362                             tag: 'button',
15363                             cls: 'btn btn-danger roo-touch-view-cancel',
15364                             html: 'Cancel'
15365                         }
15366                     },
15367                     {
15368                         tag: 'div',
15369                         cls: 'col-xs-6 text-right',
15370                         cn: {
15371                             tag: 'button',
15372                             cls: 'btn btn-success roo-touch-view-ok',
15373                             html: 'OK'
15374                         }
15375                     }
15376                 ]
15377             }
15378         ]
15379         
15380     }
15381 });
15382
15383 Roo.apply(Roo.bootstrap.ComboBox,  {
15384     
15385     touchViewTemplate : {
15386         tag: 'div',
15387         cls: 'modal fade roo-combobox-touch-view',
15388         cn: [
15389             {
15390                 tag: 'div',
15391                 cls: 'modal-dialog',
15392                 style : 'position:fixed', // we have to fix position....
15393                 cn: [
15394                     {
15395                         tag: 'div',
15396                         cls: 'modal-content',
15397                         cn: [
15398                             Roo.bootstrap.ComboBox.header,
15399                             Roo.bootstrap.ComboBox.body,
15400                             Roo.bootstrap.ComboBox.footer
15401                         ]
15402                     }
15403                 ]
15404             }
15405         ]
15406     }
15407 });/*
15408  * Based on:
15409  * Ext JS Library 1.1.1
15410  * Copyright(c) 2006-2007, Ext JS, LLC.
15411  *
15412  * Originally Released Under LGPL - original licence link has changed is not relivant.
15413  *
15414  * Fork - LGPL
15415  * <script type="text/javascript">
15416  */
15417
15418 /**
15419  * @class Roo.View
15420  * @extends Roo.util.Observable
15421  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15422  * This class also supports single and multi selection modes. <br>
15423  * Create a data model bound view:
15424  <pre><code>
15425  var store = new Roo.data.Store(...);
15426
15427  var view = new Roo.View({
15428     el : "my-element",
15429     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15430  
15431     singleSelect: true,
15432     selectedClass: "ydataview-selected",
15433     store: store
15434  });
15435
15436  // listen for node click?
15437  view.on("click", function(vw, index, node, e){
15438  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15439  });
15440
15441  // load XML data
15442  dataModel.load("foobar.xml");
15443  </code></pre>
15444  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15445  * <br><br>
15446  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15447  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15448  * 
15449  * Note: old style constructor is still suported (container, template, config)
15450  * 
15451  * @constructor
15452  * Create a new View
15453  * @param {Object} config The config object
15454  * 
15455  */
15456 Roo.View = function(config, depreciated_tpl, depreciated_config){
15457     
15458     this.parent = false;
15459     
15460     if (typeof(depreciated_tpl) == 'undefined') {
15461         // new way.. - universal constructor.
15462         Roo.apply(this, config);
15463         this.el  = Roo.get(this.el);
15464     } else {
15465         // old format..
15466         this.el  = Roo.get(config);
15467         this.tpl = depreciated_tpl;
15468         Roo.apply(this, depreciated_config);
15469     }
15470     this.wrapEl  = this.el.wrap().wrap();
15471     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15472     
15473     
15474     if(typeof(this.tpl) == "string"){
15475         this.tpl = new Roo.Template(this.tpl);
15476     } else {
15477         // support xtype ctors..
15478         this.tpl = new Roo.factory(this.tpl, Roo);
15479     }
15480     
15481     
15482     this.tpl.compile();
15483     
15484     /** @private */
15485     this.addEvents({
15486         /**
15487          * @event beforeclick
15488          * Fires before a click is processed. Returns false to cancel the default action.
15489          * @param {Roo.View} this
15490          * @param {Number} index The index of the target node
15491          * @param {HTMLElement} node The target node
15492          * @param {Roo.EventObject} e The raw event object
15493          */
15494             "beforeclick" : true,
15495         /**
15496          * @event click
15497          * Fires when a template node is clicked.
15498          * @param {Roo.View} this
15499          * @param {Number} index The index of the target node
15500          * @param {HTMLElement} node The target node
15501          * @param {Roo.EventObject} e The raw event object
15502          */
15503             "click" : true,
15504         /**
15505          * @event dblclick
15506          * Fires when a template node is double clicked.
15507          * @param {Roo.View} this
15508          * @param {Number} index The index of the target node
15509          * @param {HTMLElement} node The target node
15510          * @param {Roo.EventObject} e The raw event object
15511          */
15512             "dblclick" : true,
15513         /**
15514          * @event contextmenu
15515          * Fires when a template node is right clicked.
15516          * @param {Roo.View} this
15517          * @param {Number} index The index of the target node
15518          * @param {HTMLElement} node The target node
15519          * @param {Roo.EventObject} e The raw event object
15520          */
15521             "contextmenu" : true,
15522         /**
15523          * @event selectionchange
15524          * Fires when the selected nodes change.
15525          * @param {Roo.View} this
15526          * @param {Array} selections Array of the selected nodes
15527          */
15528             "selectionchange" : true,
15529     
15530         /**
15531          * @event beforeselect
15532          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15533          * @param {Roo.View} this
15534          * @param {HTMLElement} node The node to be selected
15535          * @param {Array} selections Array of currently selected nodes
15536          */
15537             "beforeselect" : true,
15538         /**
15539          * @event preparedata
15540          * Fires on every row to render, to allow you to change the data.
15541          * @param {Roo.View} this
15542          * @param {Object} data to be rendered (change this)
15543          */
15544           "preparedata" : true
15545           
15546           
15547         });
15548
15549
15550
15551     this.el.on({
15552         "click": this.onClick,
15553         "dblclick": this.onDblClick,
15554         "contextmenu": this.onContextMenu,
15555         scope:this
15556     });
15557
15558     this.selections = [];
15559     this.nodes = [];
15560     this.cmp = new Roo.CompositeElementLite([]);
15561     if(this.store){
15562         this.store = Roo.factory(this.store, Roo.data);
15563         this.setStore(this.store, true);
15564     }
15565     
15566     if ( this.footer && this.footer.xtype) {
15567            
15568          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15569         
15570         this.footer.dataSource = this.store;
15571         this.footer.container = fctr;
15572         this.footer = Roo.factory(this.footer, Roo);
15573         fctr.insertFirst(this.el);
15574         
15575         // this is a bit insane - as the paging toolbar seems to detach the el..
15576 //        dom.parentNode.parentNode.parentNode
15577          // they get detached?
15578     }
15579     
15580     
15581     Roo.View.superclass.constructor.call(this);
15582     
15583     
15584 };
15585
15586 Roo.extend(Roo.View, Roo.util.Observable, {
15587     
15588      /**
15589      * @cfg {Roo.data.Store} store Data store to load data from.
15590      */
15591     store : false,
15592     
15593     /**
15594      * @cfg {String|Roo.Element} el The container element.
15595      */
15596     el : '',
15597     
15598     /**
15599      * @cfg {String|Roo.Template} tpl The template used by this View 
15600      */
15601     tpl : false,
15602     /**
15603      * @cfg {String} dataName the named area of the template to use as the data area
15604      *                          Works with domtemplates roo-name="name"
15605      */
15606     dataName: false,
15607     /**
15608      * @cfg {String} selectedClass The css class to add to selected nodes
15609      */
15610     selectedClass : "x-view-selected",
15611      /**
15612      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15613      */
15614     emptyText : "",
15615     
15616     /**
15617      * @cfg {String} text to display on mask (default Loading)
15618      */
15619     mask : false,
15620     /**
15621      * @cfg {Boolean} multiSelect Allow multiple selection
15622      */
15623     multiSelect : false,
15624     /**
15625      * @cfg {Boolean} singleSelect Allow single selection
15626      */
15627     singleSelect:  false,
15628     
15629     /**
15630      * @cfg {Boolean} toggleSelect - selecting 
15631      */
15632     toggleSelect : false,
15633     
15634     /**
15635      * @cfg {Boolean} tickable - selecting 
15636      */
15637     tickable : false,
15638     
15639     /**
15640      * Returns the element this view is bound to.
15641      * @return {Roo.Element}
15642      */
15643     getEl : function(){
15644         return this.wrapEl;
15645     },
15646     
15647     
15648
15649     /**
15650      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15651      */
15652     refresh : function(){
15653         //Roo.log('refresh');
15654         var t = this.tpl;
15655         
15656         // if we are using something like 'domtemplate', then
15657         // the what gets used is:
15658         // t.applySubtemplate(NAME, data, wrapping data..)
15659         // the outer template then get' applied with
15660         //     the store 'extra data'
15661         // and the body get's added to the
15662         //      roo-name="data" node?
15663         //      <span class='roo-tpl-{name}'></span> ?????
15664         
15665         
15666         
15667         this.clearSelections();
15668         this.el.update("");
15669         var html = [];
15670         var records = this.store.getRange();
15671         if(records.length < 1) {
15672             
15673             // is this valid??  = should it render a template??
15674             
15675             this.el.update(this.emptyText);
15676             return;
15677         }
15678         var el = this.el;
15679         if (this.dataName) {
15680             this.el.update(t.apply(this.store.meta)); //????
15681             el = this.el.child('.roo-tpl-' + this.dataName);
15682         }
15683         
15684         for(var i = 0, len = records.length; i < len; i++){
15685             var data = this.prepareData(records[i].data, i, records[i]);
15686             this.fireEvent("preparedata", this, data, i, records[i]);
15687             
15688             var d = Roo.apply({}, data);
15689             
15690             if(this.tickable){
15691                 Roo.apply(d, {'roo-id' : Roo.id()});
15692                 
15693                 var _this = this;
15694             
15695                 Roo.each(this.parent.item, function(item){
15696                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15697                         return;
15698                     }
15699                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15700                 });
15701             }
15702             
15703             html[html.length] = Roo.util.Format.trim(
15704                 this.dataName ?
15705                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15706                     t.apply(d)
15707             );
15708         }
15709         
15710         
15711         
15712         el.update(html.join(""));
15713         this.nodes = el.dom.childNodes;
15714         this.updateIndexes(0);
15715     },
15716     
15717
15718     /**
15719      * Function to override to reformat the data that is sent to
15720      * the template for each node.
15721      * DEPRICATED - use the preparedata event handler.
15722      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15723      * a JSON object for an UpdateManager bound view).
15724      */
15725     prepareData : function(data, index, record)
15726     {
15727         this.fireEvent("preparedata", this, data, index, record);
15728         return data;
15729     },
15730
15731     onUpdate : function(ds, record){
15732         // Roo.log('on update');   
15733         this.clearSelections();
15734         var index = this.store.indexOf(record);
15735         var n = this.nodes[index];
15736         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15737         n.parentNode.removeChild(n);
15738         this.updateIndexes(index, index);
15739     },
15740
15741     
15742     
15743 // --------- FIXME     
15744     onAdd : function(ds, records, index)
15745     {
15746         //Roo.log(['on Add', ds, records, index] );        
15747         this.clearSelections();
15748         if(this.nodes.length == 0){
15749             this.refresh();
15750             return;
15751         }
15752         var n = this.nodes[index];
15753         for(var i = 0, len = records.length; i < len; i++){
15754             var d = this.prepareData(records[i].data, i, records[i]);
15755             if(n){
15756                 this.tpl.insertBefore(n, d);
15757             }else{
15758                 
15759                 this.tpl.append(this.el, d);
15760             }
15761         }
15762         this.updateIndexes(index);
15763     },
15764
15765     onRemove : function(ds, record, index){
15766        // Roo.log('onRemove');
15767         this.clearSelections();
15768         var el = this.dataName  ?
15769             this.el.child('.roo-tpl-' + this.dataName) :
15770             this.el; 
15771         
15772         el.dom.removeChild(this.nodes[index]);
15773         this.updateIndexes(index);
15774     },
15775
15776     /**
15777      * Refresh an individual node.
15778      * @param {Number} index
15779      */
15780     refreshNode : function(index){
15781         this.onUpdate(this.store, this.store.getAt(index));
15782     },
15783
15784     updateIndexes : function(startIndex, endIndex){
15785         var ns = this.nodes;
15786         startIndex = startIndex || 0;
15787         endIndex = endIndex || ns.length - 1;
15788         for(var i = startIndex; i <= endIndex; i++){
15789             ns[i].nodeIndex = i;
15790         }
15791     },
15792
15793     /**
15794      * Changes the data store this view uses and refresh the view.
15795      * @param {Store} store
15796      */
15797     setStore : function(store, initial){
15798         if(!initial && this.store){
15799             this.store.un("datachanged", this.refresh);
15800             this.store.un("add", this.onAdd);
15801             this.store.un("remove", this.onRemove);
15802             this.store.un("update", this.onUpdate);
15803             this.store.un("clear", this.refresh);
15804             this.store.un("beforeload", this.onBeforeLoad);
15805             this.store.un("load", this.onLoad);
15806             this.store.un("loadexception", this.onLoad);
15807         }
15808         if(store){
15809           
15810             store.on("datachanged", this.refresh, this);
15811             store.on("add", this.onAdd, this);
15812             store.on("remove", this.onRemove, this);
15813             store.on("update", this.onUpdate, this);
15814             store.on("clear", this.refresh, this);
15815             store.on("beforeload", this.onBeforeLoad, this);
15816             store.on("load", this.onLoad, this);
15817             store.on("loadexception", this.onLoad, this);
15818         }
15819         
15820         if(store){
15821             this.refresh();
15822         }
15823     },
15824     /**
15825      * onbeforeLoad - masks the loading area.
15826      *
15827      */
15828     onBeforeLoad : function(store,opts)
15829     {
15830          //Roo.log('onBeforeLoad');   
15831         if (!opts.add) {
15832             this.el.update("");
15833         }
15834         this.el.mask(this.mask ? this.mask : "Loading" ); 
15835     },
15836     onLoad : function ()
15837     {
15838         this.el.unmask();
15839     },
15840     
15841
15842     /**
15843      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15844      * @param {HTMLElement} node
15845      * @return {HTMLElement} The template node
15846      */
15847     findItemFromChild : function(node){
15848         var el = this.dataName  ?
15849             this.el.child('.roo-tpl-' + this.dataName,true) :
15850             this.el.dom; 
15851         
15852         if(!node || node.parentNode == el){
15853                     return node;
15854             }
15855             var p = node.parentNode;
15856             while(p && p != el){
15857             if(p.parentNode == el){
15858                 return p;
15859             }
15860             p = p.parentNode;
15861         }
15862             return null;
15863     },
15864
15865     /** @ignore */
15866     onClick : function(e){
15867         var item = this.findItemFromChild(e.getTarget());
15868         if(item){
15869             var index = this.indexOf(item);
15870             if(this.onItemClick(item, index, e) !== false){
15871                 this.fireEvent("click", this, index, item, e);
15872             }
15873         }else{
15874             this.clearSelections();
15875         }
15876     },
15877
15878     /** @ignore */
15879     onContextMenu : function(e){
15880         var item = this.findItemFromChild(e.getTarget());
15881         if(item){
15882             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15883         }
15884     },
15885
15886     /** @ignore */
15887     onDblClick : function(e){
15888         var item = this.findItemFromChild(e.getTarget());
15889         if(item){
15890             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15891         }
15892     },
15893
15894     onItemClick : function(item, index, e)
15895     {
15896         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15897             return false;
15898         }
15899         if (this.toggleSelect) {
15900             var m = this.isSelected(item) ? 'unselect' : 'select';
15901             //Roo.log(m);
15902             var _t = this;
15903             _t[m](item, true, false);
15904             return true;
15905         }
15906         if(this.multiSelect || this.singleSelect){
15907             if(this.multiSelect && e.shiftKey && this.lastSelection){
15908                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15909             }else{
15910                 this.select(item, this.multiSelect && e.ctrlKey);
15911                 this.lastSelection = item;
15912             }
15913             
15914             if(!this.tickable){
15915                 e.preventDefault();
15916             }
15917             
15918         }
15919         return true;
15920     },
15921
15922     /**
15923      * Get the number of selected nodes.
15924      * @return {Number}
15925      */
15926     getSelectionCount : function(){
15927         return this.selections.length;
15928     },
15929
15930     /**
15931      * Get the currently selected nodes.
15932      * @return {Array} An array of HTMLElements
15933      */
15934     getSelectedNodes : function(){
15935         return this.selections;
15936     },
15937
15938     /**
15939      * Get the indexes of the selected nodes.
15940      * @return {Array}
15941      */
15942     getSelectedIndexes : function(){
15943         var indexes = [], s = this.selections;
15944         for(var i = 0, len = s.length; i < len; i++){
15945             indexes.push(s[i].nodeIndex);
15946         }
15947         return indexes;
15948     },
15949
15950     /**
15951      * Clear all selections
15952      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15953      */
15954     clearSelections : function(suppressEvent){
15955         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15956             this.cmp.elements = this.selections;
15957             this.cmp.removeClass(this.selectedClass);
15958             this.selections = [];
15959             if(!suppressEvent){
15960                 this.fireEvent("selectionchange", this, this.selections);
15961             }
15962         }
15963     },
15964
15965     /**
15966      * Returns true if the passed node is selected
15967      * @param {HTMLElement/Number} node The node or node index
15968      * @return {Boolean}
15969      */
15970     isSelected : function(node){
15971         var s = this.selections;
15972         if(s.length < 1){
15973             return false;
15974         }
15975         node = this.getNode(node);
15976         return s.indexOf(node) !== -1;
15977     },
15978
15979     /**
15980      * Selects nodes.
15981      * @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
15982      * @param {Boolean} keepExisting (optional) true to keep existing selections
15983      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15984      */
15985     select : function(nodeInfo, keepExisting, suppressEvent){
15986         if(nodeInfo instanceof Array){
15987             if(!keepExisting){
15988                 this.clearSelections(true);
15989             }
15990             for(var i = 0, len = nodeInfo.length; i < len; i++){
15991                 this.select(nodeInfo[i], true, true);
15992             }
15993             return;
15994         } 
15995         var node = this.getNode(nodeInfo);
15996         if(!node || this.isSelected(node)){
15997             return; // already selected.
15998         }
15999         if(!keepExisting){
16000             this.clearSelections(true);
16001         }
16002         
16003         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16004             Roo.fly(node).addClass(this.selectedClass);
16005             this.selections.push(node);
16006             if(!suppressEvent){
16007                 this.fireEvent("selectionchange", this, this.selections);
16008             }
16009         }
16010         
16011         
16012     },
16013       /**
16014      * Unselects nodes.
16015      * @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
16016      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16017      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16018      */
16019     unselect : function(nodeInfo, keepExisting, suppressEvent)
16020     {
16021         if(nodeInfo instanceof Array){
16022             Roo.each(this.selections, function(s) {
16023                 this.unselect(s, nodeInfo);
16024             }, this);
16025             return;
16026         }
16027         var node = this.getNode(nodeInfo);
16028         if(!node || !this.isSelected(node)){
16029             //Roo.log("not selected");
16030             return; // not selected.
16031         }
16032         // fireevent???
16033         var ns = [];
16034         Roo.each(this.selections, function(s) {
16035             if (s == node ) {
16036                 Roo.fly(node).removeClass(this.selectedClass);
16037
16038                 return;
16039             }
16040             ns.push(s);
16041         },this);
16042         
16043         this.selections= ns;
16044         this.fireEvent("selectionchange", this, this.selections);
16045     },
16046
16047     /**
16048      * Gets a template node.
16049      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16050      * @return {HTMLElement} The node or null if it wasn't found
16051      */
16052     getNode : function(nodeInfo){
16053         if(typeof nodeInfo == "string"){
16054             return document.getElementById(nodeInfo);
16055         }else if(typeof nodeInfo == "number"){
16056             return this.nodes[nodeInfo];
16057         }
16058         return nodeInfo;
16059     },
16060
16061     /**
16062      * Gets a range template nodes.
16063      * @param {Number} startIndex
16064      * @param {Number} endIndex
16065      * @return {Array} An array of nodes
16066      */
16067     getNodes : function(start, end){
16068         var ns = this.nodes;
16069         start = start || 0;
16070         end = typeof end == "undefined" ? ns.length - 1 : end;
16071         var nodes = [];
16072         if(start <= end){
16073             for(var i = start; i <= end; i++){
16074                 nodes.push(ns[i]);
16075             }
16076         } else{
16077             for(var i = start; i >= end; i--){
16078                 nodes.push(ns[i]);
16079             }
16080         }
16081         return nodes;
16082     },
16083
16084     /**
16085      * Finds the index of the passed node
16086      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16087      * @return {Number} The index of the node or -1
16088      */
16089     indexOf : function(node){
16090         node = this.getNode(node);
16091         if(typeof node.nodeIndex == "number"){
16092             return node.nodeIndex;
16093         }
16094         var ns = this.nodes;
16095         for(var i = 0, len = ns.length; i < len; i++){
16096             if(ns[i] == node){
16097                 return i;
16098             }
16099         }
16100         return -1;
16101     }
16102 });
16103 /*
16104  * - LGPL
16105  *
16106  * based on jquery fullcalendar
16107  * 
16108  */
16109
16110 Roo.bootstrap = Roo.bootstrap || {};
16111 /**
16112  * @class Roo.bootstrap.Calendar
16113  * @extends Roo.bootstrap.Component
16114  * Bootstrap Calendar class
16115  * @cfg {Boolean} loadMask (true|false) default false
16116  * @cfg {Object} header generate the user specific header of the calendar, default false
16117
16118  * @constructor
16119  * Create a new Container
16120  * @param {Object} config The config object
16121  */
16122
16123
16124
16125 Roo.bootstrap.Calendar = function(config){
16126     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16127      this.addEvents({
16128         /**
16129              * @event select
16130              * Fires when a date is selected
16131              * @param {DatePicker} this
16132              * @param {Date} date The selected date
16133              */
16134         'select': true,
16135         /**
16136              * @event monthchange
16137              * Fires when the displayed month changes 
16138              * @param {DatePicker} this
16139              * @param {Date} date The selected month
16140              */
16141         'monthchange': true,
16142         /**
16143              * @event evententer
16144              * Fires when mouse over an event
16145              * @param {Calendar} this
16146              * @param {event} Event
16147              */
16148         'evententer': true,
16149         /**
16150              * @event eventleave
16151              * Fires when the mouse leaves an
16152              * @param {Calendar} this
16153              * @param {event}
16154              */
16155         'eventleave': true,
16156         /**
16157              * @event eventclick
16158              * Fires when the mouse click an
16159              * @param {Calendar} this
16160              * @param {event}
16161              */
16162         'eventclick': true
16163         
16164     });
16165
16166 };
16167
16168 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16169     
16170      /**
16171      * @cfg {Number} startDay
16172      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16173      */
16174     startDay : 0,
16175     
16176     loadMask : false,
16177     
16178     header : false,
16179       
16180     getAutoCreate : function(){
16181         
16182         
16183         var fc_button = function(name, corner, style, content ) {
16184             return Roo.apply({},{
16185                 tag : 'span',
16186                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16187                          (corner.length ?
16188                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16189                             ''
16190                         ),
16191                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16192                 unselectable: 'on'
16193             });
16194         };
16195         
16196         var header = {};
16197         
16198         if(!this.header){
16199             header = {
16200                 tag : 'table',
16201                 cls : 'fc-header',
16202                 style : 'width:100%',
16203                 cn : [
16204                     {
16205                         tag: 'tr',
16206                         cn : [
16207                             {
16208                                 tag : 'td',
16209                                 cls : 'fc-header-left',
16210                                 cn : [
16211                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16212                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16213                                     { tag: 'span', cls: 'fc-header-space' },
16214                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16215
16216
16217                                 ]
16218                             },
16219
16220                             {
16221                                 tag : 'td',
16222                                 cls : 'fc-header-center',
16223                                 cn : [
16224                                     {
16225                                         tag: 'span',
16226                                         cls: 'fc-header-title',
16227                                         cn : {
16228                                             tag: 'H2',
16229                                             html : 'month / year'
16230                                         }
16231                                     }
16232
16233                                 ]
16234                             },
16235                             {
16236                                 tag : 'td',
16237                                 cls : 'fc-header-right',
16238                                 cn : [
16239                               /*      fc_button('month', 'left', '', 'month' ),
16240                                     fc_button('week', '', '', 'week' ),
16241                                     fc_button('day', 'right', '', 'day' )
16242                                 */    
16243
16244                                 ]
16245                             }
16246
16247                         ]
16248                     }
16249                 ]
16250             };
16251         }
16252         
16253         header = this.header;
16254         
16255        
16256         var cal_heads = function() {
16257             var ret = [];
16258             // fixme - handle this.
16259             
16260             for (var i =0; i < Date.dayNames.length; i++) {
16261                 var d = Date.dayNames[i];
16262                 ret.push({
16263                     tag: 'th',
16264                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16265                     html : d.substring(0,3)
16266                 });
16267                 
16268             }
16269             ret[0].cls += ' fc-first';
16270             ret[6].cls += ' fc-last';
16271             return ret;
16272         };
16273         var cal_cell = function(n) {
16274             return  {
16275                 tag: 'td',
16276                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16277                 cn : [
16278                     {
16279                         cn : [
16280                             {
16281                                 cls: 'fc-day-number',
16282                                 html: 'D'
16283                             },
16284                             {
16285                                 cls: 'fc-day-content',
16286                              
16287                                 cn : [
16288                                      {
16289                                         style: 'position: relative;' // height: 17px;
16290                                     }
16291                                 ]
16292                             }
16293                             
16294                             
16295                         ]
16296                     }
16297                 ]
16298                 
16299             }
16300         };
16301         var cal_rows = function() {
16302             
16303             var ret = [];
16304             for (var r = 0; r < 6; r++) {
16305                 var row= {
16306                     tag : 'tr',
16307                     cls : 'fc-week',
16308                     cn : []
16309                 };
16310                 
16311                 for (var i =0; i < Date.dayNames.length; i++) {
16312                     var d = Date.dayNames[i];
16313                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16314
16315                 }
16316                 row.cn[0].cls+=' fc-first';
16317                 row.cn[0].cn[0].style = 'min-height:90px';
16318                 row.cn[6].cls+=' fc-last';
16319                 ret.push(row);
16320                 
16321             }
16322             ret[0].cls += ' fc-first';
16323             ret[4].cls += ' fc-prev-last';
16324             ret[5].cls += ' fc-last';
16325             return ret;
16326             
16327         };
16328         
16329         var cal_table = {
16330             tag: 'table',
16331             cls: 'fc-border-separate',
16332             style : 'width:100%',
16333             cellspacing  : 0,
16334             cn : [
16335                 { 
16336                     tag: 'thead',
16337                     cn : [
16338                         { 
16339                             tag: 'tr',
16340                             cls : 'fc-first fc-last',
16341                             cn : cal_heads()
16342                         }
16343                     ]
16344                 },
16345                 { 
16346                     tag: 'tbody',
16347                     cn : cal_rows()
16348                 }
16349                   
16350             ]
16351         };
16352          
16353          var cfg = {
16354             cls : 'fc fc-ltr',
16355             cn : [
16356                 header,
16357                 {
16358                     cls : 'fc-content',
16359                     style : "position: relative;",
16360                     cn : [
16361                         {
16362                             cls : 'fc-view fc-view-month fc-grid',
16363                             style : 'position: relative',
16364                             unselectable : 'on',
16365                             cn : [
16366                                 {
16367                                     cls : 'fc-event-container',
16368                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16369                                 },
16370                                 cal_table
16371                             ]
16372                         }
16373                     ]
16374     
16375                 }
16376            ] 
16377             
16378         };
16379         
16380          
16381         
16382         return cfg;
16383     },
16384     
16385     
16386     initEvents : function()
16387     {
16388         if(!this.store){
16389             throw "can not find store for calendar";
16390         }
16391         
16392         var mark = {
16393             tag: "div",
16394             cls:"x-dlg-mask",
16395             style: "text-align:center",
16396             cn: [
16397                 {
16398                     tag: "div",
16399                     style: "background-color:white;width:50%;margin:250 auto",
16400                     cn: [
16401                         {
16402                             tag: "img",
16403                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16404                         },
16405                         {
16406                             tag: "span",
16407                             html: "Loading"
16408                         }
16409                         
16410                     ]
16411                 }
16412             ]
16413         };
16414         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16415         
16416         var size = this.el.select('.fc-content', true).first().getSize();
16417         this.maskEl.setSize(size.width, size.height);
16418         this.maskEl.enableDisplayMode("block");
16419         if(!this.loadMask){
16420             this.maskEl.hide();
16421         }
16422         
16423         this.store = Roo.factory(this.store, Roo.data);
16424         this.store.on('load', this.onLoad, this);
16425         this.store.on('beforeload', this.onBeforeLoad, this);
16426         
16427         this.resize();
16428         
16429         this.cells = this.el.select('.fc-day',true);
16430         //Roo.log(this.cells);
16431         this.textNodes = this.el.query('.fc-day-number');
16432         this.cells.addClassOnOver('fc-state-hover');
16433         
16434         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16435         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16436         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16437         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16438         
16439         this.on('monthchange', this.onMonthChange, this);
16440         
16441         this.update(new Date().clearTime());
16442     },
16443     
16444     resize : function() {
16445         var sz  = this.el.getSize();
16446         
16447         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16448         this.el.select('.fc-day-content div',true).setHeight(34);
16449     },
16450     
16451     
16452     // private
16453     showPrevMonth : function(e){
16454         this.update(this.activeDate.add("mo", -1));
16455     },
16456     showToday : function(e){
16457         this.update(new Date().clearTime());
16458     },
16459     // private
16460     showNextMonth : function(e){
16461         this.update(this.activeDate.add("mo", 1));
16462     },
16463
16464     // private
16465     showPrevYear : function(){
16466         this.update(this.activeDate.add("y", -1));
16467     },
16468
16469     // private
16470     showNextYear : function(){
16471         this.update(this.activeDate.add("y", 1));
16472     },
16473
16474     
16475    // private
16476     update : function(date)
16477     {
16478         var vd = this.activeDate;
16479         this.activeDate = date;
16480 //        if(vd && this.el){
16481 //            var t = date.getTime();
16482 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16483 //                Roo.log('using add remove');
16484 //                
16485 //                this.fireEvent('monthchange', this, date);
16486 //                
16487 //                this.cells.removeClass("fc-state-highlight");
16488 //                this.cells.each(function(c){
16489 //                   if(c.dateValue == t){
16490 //                       c.addClass("fc-state-highlight");
16491 //                       setTimeout(function(){
16492 //                            try{c.dom.firstChild.focus();}catch(e){}
16493 //                       }, 50);
16494 //                       return false;
16495 //                   }
16496 //                   return true;
16497 //                });
16498 //                return;
16499 //            }
16500 //        }
16501         
16502         var days = date.getDaysInMonth();
16503         
16504         var firstOfMonth = date.getFirstDateOfMonth();
16505         var startingPos = firstOfMonth.getDay()-this.startDay;
16506         
16507         if(startingPos < this.startDay){
16508             startingPos += 7;
16509         }
16510         
16511         var pm = date.add(Date.MONTH, -1);
16512         var prevStart = pm.getDaysInMonth()-startingPos;
16513 //        
16514         this.cells = this.el.select('.fc-day',true);
16515         this.textNodes = this.el.query('.fc-day-number');
16516         this.cells.addClassOnOver('fc-state-hover');
16517         
16518         var cells = this.cells.elements;
16519         var textEls = this.textNodes;
16520         
16521         Roo.each(cells, function(cell){
16522             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16523         });
16524         
16525         days += startingPos;
16526
16527         // convert everything to numbers so it's fast
16528         var day = 86400000;
16529         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16530         //Roo.log(d);
16531         //Roo.log(pm);
16532         //Roo.log(prevStart);
16533         
16534         var today = new Date().clearTime().getTime();
16535         var sel = date.clearTime().getTime();
16536         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16537         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16538         var ddMatch = this.disabledDatesRE;
16539         var ddText = this.disabledDatesText;
16540         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16541         var ddaysText = this.disabledDaysText;
16542         var format = this.format;
16543         
16544         var setCellClass = function(cal, cell){
16545             cell.row = 0;
16546             cell.events = [];
16547             cell.more = [];
16548             //Roo.log('set Cell Class');
16549             cell.title = "";
16550             var t = d.getTime();
16551             
16552             //Roo.log(d);
16553             
16554             cell.dateValue = t;
16555             if(t == today){
16556                 cell.className += " fc-today";
16557                 cell.className += " fc-state-highlight";
16558                 cell.title = cal.todayText;
16559             }
16560             if(t == sel){
16561                 // disable highlight in other month..
16562                 //cell.className += " fc-state-highlight";
16563                 
16564             }
16565             // disabling
16566             if(t < min) {
16567                 cell.className = " fc-state-disabled";
16568                 cell.title = cal.minText;
16569                 return;
16570             }
16571             if(t > max) {
16572                 cell.className = " fc-state-disabled";
16573                 cell.title = cal.maxText;
16574                 return;
16575             }
16576             if(ddays){
16577                 if(ddays.indexOf(d.getDay()) != -1){
16578                     cell.title = ddaysText;
16579                     cell.className = " fc-state-disabled";
16580                 }
16581             }
16582             if(ddMatch && format){
16583                 var fvalue = d.dateFormat(format);
16584                 if(ddMatch.test(fvalue)){
16585                     cell.title = ddText.replace("%0", fvalue);
16586                     cell.className = " fc-state-disabled";
16587                 }
16588             }
16589             
16590             if (!cell.initialClassName) {
16591                 cell.initialClassName = cell.dom.className;
16592             }
16593             
16594             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16595         };
16596
16597         var i = 0;
16598         
16599         for(; i < startingPos; i++) {
16600             textEls[i].innerHTML = (++prevStart);
16601             d.setDate(d.getDate()+1);
16602             
16603             cells[i].className = "fc-past fc-other-month";
16604             setCellClass(this, cells[i]);
16605         }
16606         
16607         var intDay = 0;
16608         
16609         for(; i < days; i++){
16610             intDay = i - startingPos + 1;
16611             textEls[i].innerHTML = (intDay);
16612             d.setDate(d.getDate()+1);
16613             
16614             cells[i].className = ''; // "x-date-active";
16615             setCellClass(this, cells[i]);
16616         }
16617         var extraDays = 0;
16618         
16619         for(; i < 42; i++) {
16620             textEls[i].innerHTML = (++extraDays);
16621             d.setDate(d.getDate()+1);
16622             
16623             cells[i].className = "fc-future fc-other-month";
16624             setCellClass(this, cells[i]);
16625         }
16626         
16627         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16628         
16629         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16630         
16631         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16632         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16633         
16634         if(totalRows != 6){
16635             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16636             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16637         }
16638         
16639         this.fireEvent('monthchange', this, date);
16640         
16641         
16642         /*
16643         if(!this.internalRender){
16644             var main = this.el.dom.firstChild;
16645             var w = main.offsetWidth;
16646             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16647             Roo.fly(main).setWidth(w);
16648             this.internalRender = true;
16649             // opera does not respect the auto grow header center column
16650             // then, after it gets a width opera refuses to recalculate
16651             // without a second pass
16652             if(Roo.isOpera && !this.secondPass){
16653                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16654                 this.secondPass = true;
16655                 this.update.defer(10, this, [date]);
16656             }
16657         }
16658         */
16659         
16660     },
16661     
16662     findCell : function(dt) {
16663         dt = dt.clearTime().getTime();
16664         var ret = false;
16665         this.cells.each(function(c){
16666             //Roo.log("check " +c.dateValue + '?=' + dt);
16667             if(c.dateValue == dt){
16668                 ret = c;
16669                 return false;
16670             }
16671             return true;
16672         });
16673         
16674         return ret;
16675     },
16676     
16677     findCells : function(ev) {
16678         var s = ev.start.clone().clearTime().getTime();
16679        // Roo.log(s);
16680         var e= ev.end.clone().clearTime().getTime();
16681        // Roo.log(e);
16682         var ret = [];
16683         this.cells.each(function(c){
16684              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16685             
16686             if(c.dateValue > e){
16687                 return ;
16688             }
16689             if(c.dateValue < s){
16690                 return ;
16691             }
16692             ret.push(c);
16693         });
16694         
16695         return ret;    
16696     },
16697     
16698 //    findBestRow: function(cells)
16699 //    {
16700 //        var ret = 0;
16701 //        
16702 //        for (var i =0 ; i < cells.length;i++) {
16703 //            ret  = Math.max(cells[i].rows || 0,ret);
16704 //        }
16705 //        return ret;
16706 //        
16707 //    },
16708     
16709     
16710     addItem : function(ev)
16711     {
16712         // look for vertical location slot in
16713         var cells = this.findCells(ev);
16714         
16715 //        ev.row = this.findBestRow(cells);
16716         
16717         // work out the location.
16718         
16719         var crow = false;
16720         var rows = [];
16721         for(var i =0; i < cells.length; i++) {
16722             
16723             cells[i].row = cells[0].row;
16724             
16725             if(i == 0){
16726                 cells[i].row = cells[i].row + 1;
16727             }
16728             
16729             if (!crow) {
16730                 crow = {
16731                     start : cells[i],
16732                     end :  cells[i]
16733                 };
16734                 continue;
16735             }
16736             if (crow.start.getY() == cells[i].getY()) {
16737                 // on same row.
16738                 crow.end = cells[i];
16739                 continue;
16740             }
16741             // different row.
16742             rows.push(crow);
16743             crow = {
16744                 start: cells[i],
16745                 end : cells[i]
16746             };
16747             
16748         }
16749         
16750         rows.push(crow);
16751         ev.els = [];
16752         ev.rows = rows;
16753         ev.cells = cells;
16754         
16755         cells[0].events.push(ev);
16756         
16757         this.calevents.push(ev);
16758     },
16759     
16760     clearEvents: function() {
16761         
16762         if(!this.calevents){
16763             return;
16764         }
16765         
16766         Roo.each(this.cells.elements, function(c){
16767             c.row = 0;
16768             c.events = [];
16769             c.more = [];
16770         });
16771         
16772         Roo.each(this.calevents, function(e) {
16773             Roo.each(e.els, function(el) {
16774                 el.un('mouseenter' ,this.onEventEnter, this);
16775                 el.un('mouseleave' ,this.onEventLeave, this);
16776                 el.remove();
16777             },this);
16778         },this);
16779         
16780         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16781             e.remove();
16782         });
16783         
16784     },
16785     
16786     renderEvents: function()
16787     {   
16788         var _this = this;
16789         
16790         this.cells.each(function(c) {
16791             
16792             if(c.row < 5){
16793                 return;
16794             }
16795             
16796             var ev = c.events;
16797             
16798             var r = 4;
16799             if(c.row != c.events.length){
16800                 r = 4 - (4 - (c.row - c.events.length));
16801             }
16802             
16803             c.events = ev.slice(0, r);
16804             c.more = ev.slice(r);
16805             
16806             if(c.more.length && c.more.length == 1){
16807                 c.events.push(c.more.pop());
16808             }
16809             
16810             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16811             
16812         });
16813             
16814         this.cells.each(function(c) {
16815             
16816             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16817             
16818             
16819             for (var e = 0; e < c.events.length; e++){
16820                 var ev = c.events[e];
16821                 var rows = ev.rows;
16822                 
16823                 for(var i = 0; i < rows.length; i++) {
16824                 
16825                     // how many rows should it span..
16826
16827                     var  cfg = {
16828                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16829                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16830
16831                         unselectable : "on",
16832                         cn : [
16833                             {
16834                                 cls: 'fc-event-inner',
16835                                 cn : [
16836     //                                {
16837     //                                  tag:'span',
16838     //                                  cls: 'fc-event-time',
16839     //                                  html : cells.length > 1 ? '' : ev.time
16840     //                                },
16841                                     {
16842                                       tag:'span',
16843                                       cls: 'fc-event-title',
16844                                       html : String.format('{0}', ev.title)
16845                                     }
16846
16847
16848                                 ]
16849                             },
16850                             {
16851                                 cls: 'ui-resizable-handle ui-resizable-e',
16852                                 html : '&nbsp;&nbsp;&nbsp'
16853                             }
16854
16855                         ]
16856                     };
16857
16858                     if (i == 0) {
16859                         cfg.cls += ' fc-event-start';
16860                     }
16861                     if ((i+1) == rows.length) {
16862                         cfg.cls += ' fc-event-end';
16863                     }
16864
16865                     var ctr = _this.el.select('.fc-event-container',true).first();
16866                     var cg = ctr.createChild(cfg);
16867
16868                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16869                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16870
16871                     var r = (c.more.length) ? 1 : 0;
16872                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16873                     cg.setWidth(ebox.right - sbox.x -2);
16874
16875                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16876                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16877                     cg.on('click', _this.onEventClick, _this, ev);
16878
16879                     ev.els.push(cg);
16880                     
16881                 }
16882                 
16883             }
16884             
16885             
16886             if(c.more.length){
16887                 var  cfg = {
16888                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16889                     style : 'position: absolute',
16890                     unselectable : "on",
16891                     cn : [
16892                         {
16893                             cls: 'fc-event-inner',
16894                             cn : [
16895                                 {
16896                                   tag:'span',
16897                                   cls: 'fc-event-title',
16898                                   html : 'More'
16899                                 }
16900
16901
16902                             ]
16903                         },
16904                         {
16905                             cls: 'ui-resizable-handle ui-resizable-e',
16906                             html : '&nbsp;&nbsp;&nbsp'
16907                         }
16908
16909                     ]
16910                 };
16911
16912                 var ctr = _this.el.select('.fc-event-container',true).first();
16913                 var cg = ctr.createChild(cfg);
16914
16915                 var sbox = c.select('.fc-day-content',true).first().getBox();
16916                 var ebox = c.select('.fc-day-content',true).first().getBox();
16917                 //Roo.log(cg);
16918                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16919                 cg.setWidth(ebox.right - sbox.x -2);
16920
16921                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16922                 
16923             }
16924             
16925         });
16926         
16927         
16928         
16929     },
16930     
16931     onEventEnter: function (e, el,event,d) {
16932         this.fireEvent('evententer', this, el, event);
16933     },
16934     
16935     onEventLeave: function (e, el,event,d) {
16936         this.fireEvent('eventleave', this, el, event);
16937     },
16938     
16939     onEventClick: function (e, el,event,d) {
16940         this.fireEvent('eventclick', this, el, event);
16941     },
16942     
16943     onMonthChange: function () {
16944         this.store.load();
16945     },
16946     
16947     onMoreEventClick: function(e, el, more)
16948     {
16949         var _this = this;
16950         
16951         this.calpopover.placement = 'right';
16952         this.calpopover.setTitle('More');
16953         
16954         this.calpopover.setContent('');
16955         
16956         var ctr = this.calpopover.el.select('.popover-content', true).first();
16957         
16958         Roo.each(more, function(m){
16959             var cfg = {
16960                 cls : 'fc-event-hori fc-event-draggable',
16961                 html : m.title
16962             };
16963             var cg = ctr.createChild(cfg);
16964             
16965             cg.on('click', _this.onEventClick, _this, m);
16966         });
16967         
16968         this.calpopover.show(el);
16969         
16970         
16971     },
16972     
16973     onLoad: function () 
16974     {   
16975         this.calevents = [];
16976         var cal = this;
16977         
16978         if(this.store.getCount() > 0){
16979             this.store.data.each(function(d){
16980                cal.addItem({
16981                     id : d.data.id,
16982                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16983                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16984                     time : d.data.start_time,
16985                     title : d.data.title,
16986                     description : d.data.description,
16987                     venue : d.data.venue
16988                 });
16989             });
16990         }
16991         
16992         this.renderEvents();
16993         
16994         if(this.calevents.length && this.loadMask){
16995             this.maskEl.hide();
16996         }
16997     },
16998     
16999     onBeforeLoad: function()
17000     {
17001         this.clearEvents();
17002         if(this.loadMask){
17003             this.maskEl.show();
17004         }
17005     }
17006 });
17007
17008  
17009  /*
17010  * - LGPL
17011  *
17012  * element
17013  * 
17014  */
17015
17016 /**
17017  * @class Roo.bootstrap.Popover
17018  * @extends Roo.bootstrap.Component
17019  * Bootstrap Popover class
17020  * @cfg {String} html contents of the popover   (or false to use children..)
17021  * @cfg {String} title of popover (or false to hide)
17022  * @cfg {String} placement how it is placed
17023  * @cfg {String} trigger click || hover (or false to trigger manually)
17024  * @cfg {String} over what (parent or false to trigger manually.)
17025  * @cfg {Number} delay - delay before showing
17026  
17027  * @constructor
17028  * Create a new Popover
17029  * @param {Object} config The config object
17030  */
17031
17032 Roo.bootstrap.Popover = function(config){
17033     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17034     
17035     this.addEvents({
17036         // raw events
17037          /**
17038          * @event show
17039          * After the popover show
17040          * 
17041          * @param {Roo.bootstrap.Popover} this
17042          */
17043         "show" : true,
17044         /**
17045          * @event hide
17046          * After the popover hide
17047          * 
17048          * @param {Roo.bootstrap.Popover} this
17049          */
17050         "hide" : true
17051     });
17052 };
17053
17054 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17055     
17056     title: 'Fill in a title',
17057     html: false,
17058     
17059     placement : 'right',
17060     trigger : 'hover', // hover
17061     
17062     delay : 0,
17063     
17064     over: 'parent',
17065     
17066     can_build_overlaid : false,
17067     
17068     getChildContainer : function()
17069     {
17070         return this.el.select('.popover-content',true).first();
17071     },
17072     
17073     getAutoCreate : function(){
17074          
17075         var cfg = {
17076            cls : 'popover roo-dynamic',
17077            style: 'display:block',
17078            cn : [
17079                 {
17080                     cls : 'arrow'
17081                 },
17082                 {
17083                     cls : 'popover-inner',
17084                     cn : [
17085                         {
17086                             tag: 'h3',
17087                             cls: 'popover-title',
17088                             html : this.title
17089                         },
17090                         {
17091                             cls : 'popover-content',
17092                             html : this.html
17093                         }
17094                     ]
17095                     
17096                 }
17097            ]
17098         };
17099         
17100         return cfg;
17101     },
17102     setTitle: function(str)
17103     {
17104         this.title = str;
17105         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17106     },
17107     setContent: function(str)
17108     {
17109         this.html = str;
17110         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17111     },
17112     // as it get's added to the bottom of the page.
17113     onRender : function(ct, position)
17114     {
17115         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17116         if(!this.el){
17117             var cfg = Roo.apply({},  this.getAutoCreate());
17118             cfg.id = Roo.id();
17119             
17120             if (this.cls) {
17121                 cfg.cls += ' ' + this.cls;
17122             }
17123             if (this.style) {
17124                 cfg.style = this.style;
17125             }
17126             //Roo.log("adding to ");
17127             this.el = Roo.get(document.body).createChild(cfg, position);
17128 //            Roo.log(this.el);
17129         }
17130         this.initEvents();
17131     },
17132     
17133     initEvents : function()
17134     {
17135         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17136         this.el.enableDisplayMode('block');
17137         this.el.hide();
17138         if (this.over === false) {
17139             return; 
17140         }
17141         if (this.triggers === false) {
17142             return;
17143         }
17144         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17145         var triggers = this.trigger ? this.trigger.split(' ') : [];
17146         Roo.each(triggers, function(trigger) {
17147         
17148             if (trigger == 'click') {
17149                 on_el.on('click', this.toggle, this);
17150             } else if (trigger != 'manual') {
17151                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17152                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17153       
17154                 on_el.on(eventIn  ,this.enter, this);
17155                 on_el.on(eventOut, this.leave, this);
17156             }
17157         }, this);
17158         
17159     },
17160     
17161     
17162     // private
17163     timeout : null,
17164     hoverState : null,
17165     
17166     toggle : function () {
17167         this.hoverState == 'in' ? this.leave() : this.enter();
17168     },
17169     
17170     enter : function () {
17171         
17172         clearTimeout(this.timeout);
17173     
17174         this.hoverState = 'in';
17175     
17176         if (!this.delay || !this.delay.show) {
17177             this.show();
17178             return;
17179         }
17180         var _t = this;
17181         this.timeout = setTimeout(function () {
17182             if (_t.hoverState == 'in') {
17183                 _t.show();
17184             }
17185         }, this.delay.show)
17186     },
17187     
17188     leave : function() {
17189         clearTimeout(this.timeout);
17190     
17191         this.hoverState = 'out';
17192     
17193         if (!this.delay || !this.delay.hide) {
17194             this.hide();
17195             return;
17196         }
17197         var _t = this;
17198         this.timeout = setTimeout(function () {
17199             if (_t.hoverState == 'out') {
17200                 _t.hide();
17201             }
17202         }, this.delay.hide)
17203     },
17204     
17205     show : function (on_el)
17206     {
17207         if (!on_el) {
17208             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17209         }
17210         
17211         // set content.
17212         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17213         if (this.html !== false) {
17214             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17215         }
17216         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17217         if (!this.title.length) {
17218             this.el.select('.popover-title',true).hide();
17219         }
17220         
17221         var placement = typeof this.placement == 'function' ?
17222             this.placement.call(this, this.el, on_el) :
17223             this.placement;
17224             
17225         var autoToken = /\s?auto?\s?/i;
17226         var autoPlace = autoToken.test(placement);
17227         if (autoPlace) {
17228             placement = placement.replace(autoToken, '') || 'top';
17229         }
17230         
17231         //this.el.detach()
17232         //this.el.setXY([0,0]);
17233         this.el.show();
17234         this.el.dom.style.display='block';
17235         this.el.addClass(placement);
17236         
17237         //this.el.appendTo(on_el);
17238         
17239         var p = this.getPosition();
17240         var box = this.el.getBox();
17241         
17242         if (autoPlace) {
17243             // fixme..
17244         }
17245         var align = Roo.bootstrap.Popover.alignment[placement];
17246         this.el.alignTo(on_el, align[0],align[1]);
17247         //var arrow = this.el.select('.arrow',true).first();
17248         //arrow.set(align[2], 
17249         
17250         this.el.addClass('in');
17251         
17252         
17253         if (this.el.hasClass('fade')) {
17254             // fade it?
17255         }
17256         
17257         this.hoverState = 'in';
17258         
17259         this.fireEvent('show', this);
17260         
17261     },
17262     hide : function()
17263     {
17264         this.el.setXY([0,0]);
17265         this.el.removeClass('in');
17266         this.el.hide();
17267         this.hoverState = null;
17268         
17269         this.fireEvent('hide', this);
17270     }
17271     
17272 });
17273
17274 Roo.bootstrap.Popover.alignment = {
17275     'left' : ['r-l', [-10,0], 'right'],
17276     'right' : ['l-r', [10,0], 'left'],
17277     'bottom' : ['t-b', [0,10], 'top'],
17278     'top' : [ 'b-t', [0,-10], 'bottom']
17279 };
17280
17281  /*
17282  * - LGPL
17283  *
17284  * Progress
17285  * 
17286  */
17287
17288 /**
17289  * @class Roo.bootstrap.Progress
17290  * @extends Roo.bootstrap.Component
17291  * Bootstrap Progress class
17292  * @cfg {Boolean} striped striped of the progress bar
17293  * @cfg {Boolean} active animated of the progress bar
17294  * 
17295  * 
17296  * @constructor
17297  * Create a new Progress
17298  * @param {Object} config The config object
17299  */
17300
17301 Roo.bootstrap.Progress = function(config){
17302     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17303 };
17304
17305 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17306     
17307     striped : false,
17308     active: false,
17309     
17310     getAutoCreate : function(){
17311         var cfg = {
17312             tag: 'div',
17313             cls: 'progress'
17314         };
17315         
17316         
17317         if(this.striped){
17318             cfg.cls += ' progress-striped';
17319         }
17320       
17321         if(this.active){
17322             cfg.cls += ' active';
17323         }
17324         
17325         
17326         return cfg;
17327     }
17328    
17329 });
17330
17331  
17332
17333  /*
17334  * - LGPL
17335  *
17336  * ProgressBar
17337  * 
17338  */
17339
17340 /**
17341  * @class Roo.bootstrap.ProgressBar
17342  * @extends Roo.bootstrap.Component
17343  * Bootstrap ProgressBar class
17344  * @cfg {Number} aria_valuenow aria-value now
17345  * @cfg {Number} aria_valuemin aria-value min
17346  * @cfg {Number} aria_valuemax aria-value max
17347  * @cfg {String} label label for the progress bar
17348  * @cfg {String} panel (success | info | warning | danger )
17349  * @cfg {String} role role of the progress bar
17350  * @cfg {String} sr_only text
17351  * 
17352  * 
17353  * @constructor
17354  * Create a new ProgressBar
17355  * @param {Object} config The config object
17356  */
17357
17358 Roo.bootstrap.ProgressBar = function(config){
17359     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17360 };
17361
17362 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17363     
17364     aria_valuenow : 0,
17365     aria_valuemin : 0,
17366     aria_valuemax : 100,
17367     label : false,
17368     panel : false,
17369     role : false,
17370     sr_only: false,
17371     
17372     getAutoCreate : function()
17373     {
17374         
17375         var cfg = {
17376             tag: 'div',
17377             cls: 'progress-bar',
17378             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17379         };
17380         
17381         if(this.sr_only){
17382             cfg.cn = {
17383                 tag: 'span',
17384                 cls: 'sr-only',
17385                 html: this.sr_only
17386             }
17387         }
17388         
17389         if(this.role){
17390             cfg.role = this.role;
17391         }
17392         
17393         if(this.aria_valuenow){
17394             cfg['aria-valuenow'] = this.aria_valuenow;
17395         }
17396         
17397         if(this.aria_valuemin){
17398             cfg['aria-valuemin'] = this.aria_valuemin;
17399         }
17400         
17401         if(this.aria_valuemax){
17402             cfg['aria-valuemax'] = this.aria_valuemax;
17403         }
17404         
17405         if(this.label && !this.sr_only){
17406             cfg.html = this.label;
17407         }
17408         
17409         if(this.panel){
17410             cfg.cls += ' progress-bar-' + this.panel;
17411         }
17412         
17413         return cfg;
17414     },
17415     
17416     update : function(aria_valuenow)
17417     {
17418         this.aria_valuenow = aria_valuenow;
17419         
17420         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17421     }
17422    
17423 });
17424
17425  
17426
17427  /*
17428  * - LGPL
17429  *
17430  * column
17431  * 
17432  */
17433
17434 /**
17435  * @class Roo.bootstrap.TabGroup
17436  * @extends Roo.bootstrap.Column
17437  * Bootstrap Column class
17438  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17439  * @cfg {Boolean} carousel true to make the group behave like a carousel
17440  * @cfg {Boolean} bullets show bullets for the panels
17441  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17442  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17443  * @cfg {Boolean} showarrow (true|false) show arrow default true
17444  * 
17445  * @constructor
17446  * Create a new TabGroup
17447  * @param {Object} config The config object
17448  */
17449
17450 Roo.bootstrap.TabGroup = function(config){
17451     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17452     if (!this.navId) {
17453         this.navId = Roo.id();
17454     }
17455     this.tabs = [];
17456     Roo.bootstrap.TabGroup.register(this);
17457     
17458 };
17459
17460 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17461     
17462     carousel : false,
17463     transition : false,
17464     bullets : 0,
17465     timer : 0,
17466     autoslide : false,
17467     slideFn : false,
17468     slideOnTouch : false,
17469     showarrow : true,
17470     
17471     getAutoCreate : function()
17472     {
17473         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17474         
17475         cfg.cls += ' tab-content';
17476         
17477         if (this.carousel) {
17478             cfg.cls += ' carousel slide';
17479             
17480             cfg.cn = [{
17481                cls : 'carousel-inner',
17482                cn : []
17483             }];
17484         
17485             if(this.bullets  && !Roo.isTouch){
17486                 
17487                 var bullets = {
17488                     cls : 'carousel-bullets',
17489                     cn : []
17490                 };
17491                
17492                 if(this.bullets_cls){
17493                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17494                 }
17495                 
17496                 bullets.cn.push({
17497                     cls : 'clear'
17498                 });
17499                 
17500                 cfg.cn[0].cn.push(bullets);
17501             }
17502             
17503             if(this.showarrow){
17504                 cfg.cn[0].cn.push({
17505                     tag : 'div',
17506                     class : 'carousel-arrow',
17507                     cn : [
17508                         {
17509                             tag : 'div',
17510                             class : 'carousel-prev',
17511                             cn : [
17512                                 {
17513                                     tag : 'i',
17514                                     class : 'fa fa-chevron-left'
17515                                 }
17516                             ]
17517                         },
17518                         {
17519                             tag : 'div',
17520                             class : 'carousel-next',
17521                             cn : [
17522                                 {
17523                                     tag : 'i',
17524                                     class : 'fa fa-chevron-right'
17525                                 }
17526                             ]
17527                         }
17528                     ]
17529                 });
17530             }
17531             
17532         }
17533         
17534         return cfg;
17535     },
17536     
17537     initEvents:  function()
17538     {
17539 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17540 //            this.el.on("touchstart", this.onTouchStart, this);
17541 //        }
17542         
17543         if(this.autoslide){
17544             var _this = this;
17545             
17546             this.slideFn = window.setInterval(function() {
17547                 _this.showPanelNext();
17548             }, this.timer);
17549         }
17550         
17551         if(this.showarrow){
17552             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17553             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17554         }
17555         
17556         
17557     },
17558     
17559 //    onTouchStart : function(e, el, o)
17560 //    {
17561 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17562 //            return;
17563 //        }
17564 //        
17565 //        this.showPanelNext();
17566 //    },
17567     
17568     
17569     getChildContainer : function()
17570     {
17571         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17572     },
17573     
17574     /**
17575     * register a Navigation item
17576     * @param {Roo.bootstrap.NavItem} the navitem to add
17577     */
17578     register : function(item)
17579     {
17580         this.tabs.push( item);
17581         item.navId = this.navId; // not really needed..
17582         this.addBullet();
17583     
17584     },
17585     
17586     getActivePanel : function()
17587     {
17588         var r = false;
17589         Roo.each(this.tabs, function(t) {
17590             if (t.active) {
17591                 r = t;
17592                 return false;
17593             }
17594             return null;
17595         });
17596         return r;
17597         
17598     },
17599     getPanelByName : function(n)
17600     {
17601         var r = false;
17602         Roo.each(this.tabs, function(t) {
17603             if (t.tabId == n) {
17604                 r = t;
17605                 return false;
17606             }
17607             return null;
17608         });
17609         return r;
17610     },
17611     indexOfPanel : function(p)
17612     {
17613         var r = false;
17614         Roo.each(this.tabs, function(t,i) {
17615             if (t.tabId == p.tabId) {
17616                 r = i;
17617                 return false;
17618             }
17619             return null;
17620         });
17621         return r;
17622     },
17623     /**
17624      * show a specific panel
17625      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17626      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17627      */
17628     showPanel : function (pan)
17629     {
17630         if(this.transition || typeof(pan) == 'undefined'){
17631             Roo.log("waiting for the transitionend");
17632             return;
17633         }
17634         
17635         if (typeof(pan) == 'number') {
17636             pan = this.tabs[pan];
17637         }
17638         
17639         if (typeof(pan) == 'string') {
17640             pan = this.getPanelByName(pan);
17641         }
17642         
17643         var cur = this.getActivePanel();
17644         
17645         if(!pan || !cur){
17646             Roo.log('pan or acitve pan is undefined');
17647             return false;
17648         }
17649         
17650         if (pan.tabId == this.getActivePanel().tabId) {
17651             return true;
17652         }
17653         
17654         if (false === cur.fireEvent('beforedeactivate')) {
17655             return false;
17656         }
17657         
17658         if(this.bullets > 0 && !Roo.isTouch){
17659             this.setActiveBullet(this.indexOfPanel(pan));
17660         }
17661         
17662         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17663             
17664             this.transition = true;
17665             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17666             var lr = dir == 'next' ? 'left' : 'right';
17667             pan.el.addClass(dir); // or prev
17668             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17669             cur.el.addClass(lr); // or right
17670             pan.el.addClass(lr);
17671             
17672             var _this = this;
17673             cur.el.on('transitionend', function() {
17674                 Roo.log("trans end?");
17675                 
17676                 pan.el.removeClass([lr,dir]);
17677                 pan.setActive(true);
17678                 
17679                 cur.el.removeClass([lr]);
17680                 cur.setActive(false);
17681                 
17682                 _this.transition = false;
17683                 
17684             }, this, { single:  true } );
17685             
17686             return true;
17687         }
17688         
17689         cur.setActive(false);
17690         pan.setActive(true);
17691         
17692         return true;
17693         
17694     },
17695     showPanelNext : function()
17696     {
17697         var i = this.indexOfPanel(this.getActivePanel());
17698         
17699         if (i >= this.tabs.length - 1 && !this.autoslide) {
17700             return;
17701         }
17702         
17703         if (i >= this.tabs.length - 1 && this.autoslide) {
17704             i = -1;
17705         }
17706         
17707         this.showPanel(this.tabs[i+1]);
17708     },
17709     
17710     showPanelPrev : function()
17711     {
17712         var i = this.indexOfPanel(this.getActivePanel());
17713         
17714         if (i  < 1 && !this.autoslide) {
17715             return;
17716         }
17717         
17718         if (i < 1 && this.autoslide) {
17719             i = this.tabs.length;
17720         }
17721         
17722         this.showPanel(this.tabs[i-1]);
17723     },
17724     
17725     
17726     addBullet: function()
17727     {
17728         if(!this.bullets || Roo.isTouch){
17729             return;
17730         }
17731         var ctr = this.el.select('.carousel-bullets',true).first();
17732         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17733         var bullet = ctr.createChild({
17734             cls : 'bullet bullet-' + i
17735         },ctr.dom.lastChild);
17736         
17737         
17738         var _this = this;
17739         
17740         bullet.on('click', (function(e, el, o, ii, t){
17741
17742             e.preventDefault();
17743
17744             this.showPanel(ii);
17745
17746             if(this.autoslide && this.slideFn){
17747                 clearInterval(this.slideFn);
17748                 this.slideFn = window.setInterval(function() {
17749                     _this.showPanelNext();
17750                 }, this.timer);
17751             }
17752
17753         }).createDelegate(this, [i, bullet], true));
17754                 
17755         
17756     },
17757      
17758     setActiveBullet : function(i)
17759     {
17760         if(Roo.isTouch){
17761             return;
17762         }
17763         
17764         Roo.each(this.el.select('.bullet', true).elements, function(el){
17765             el.removeClass('selected');
17766         });
17767
17768         var bullet = this.el.select('.bullet-' + i, true).first();
17769         
17770         if(!bullet){
17771             return;
17772         }
17773         
17774         bullet.addClass('selected');
17775     }
17776     
17777     
17778   
17779 });
17780
17781  
17782
17783  
17784  
17785 Roo.apply(Roo.bootstrap.TabGroup, {
17786     
17787     groups: {},
17788      /**
17789     * register a Navigation Group
17790     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17791     */
17792     register : function(navgrp)
17793     {
17794         this.groups[navgrp.navId] = navgrp;
17795         
17796     },
17797     /**
17798     * fetch a Navigation Group based on the navigation ID
17799     * if one does not exist , it will get created.
17800     * @param {string} the navgroup to add
17801     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17802     */
17803     get: function(navId) {
17804         if (typeof(this.groups[navId]) == 'undefined') {
17805             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17806         }
17807         return this.groups[navId] ;
17808     }
17809     
17810     
17811     
17812 });
17813
17814  /*
17815  * - LGPL
17816  *
17817  * TabPanel
17818  * 
17819  */
17820
17821 /**
17822  * @class Roo.bootstrap.TabPanel
17823  * @extends Roo.bootstrap.Component
17824  * Bootstrap TabPanel class
17825  * @cfg {Boolean} active panel active
17826  * @cfg {String} html panel content
17827  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17828  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17829  * @cfg {String} href click to link..
17830  * 
17831  * 
17832  * @constructor
17833  * Create a new TabPanel
17834  * @param {Object} config The config object
17835  */
17836
17837 Roo.bootstrap.TabPanel = function(config){
17838     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17839     this.addEvents({
17840         /**
17841              * @event changed
17842              * Fires when the active status changes
17843              * @param {Roo.bootstrap.TabPanel} this
17844              * @param {Boolean} state the new state
17845             
17846          */
17847         'changed': true,
17848         /**
17849              * @event beforedeactivate
17850              * Fires before a tab is de-activated - can be used to do validation on a form.
17851              * @param {Roo.bootstrap.TabPanel} this
17852              * @return {Boolean} false if there is an error
17853             
17854          */
17855         'beforedeactivate': true
17856      });
17857     
17858     this.tabId = this.tabId || Roo.id();
17859   
17860 };
17861
17862 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17863     
17864     active: false,
17865     html: false,
17866     tabId: false,
17867     navId : false,
17868     href : '',
17869     
17870     getAutoCreate : function(){
17871         var cfg = {
17872             tag: 'div',
17873             // item is needed for carousel - not sure if it has any effect otherwise
17874             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17875             html: this.html || ''
17876         };
17877         
17878         if(this.active){
17879             cfg.cls += ' active';
17880         }
17881         
17882         if(this.tabId){
17883             cfg.tabId = this.tabId;
17884         }
17885         
17886         
17887         return cfg;
17888     },
17889     
17890     initEvents:  function()
17891     {
17892         var p = this.parent();
17893         
17894         this.navId = this.navId || p.navId;
17895         
17896         if (typeof(this.navId) != 'undefined') {
17897             // not really needed.. but just in case.. parent should be a NavGroup.
17898             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17899             
17900             tg.register(this);
17901             
17902             var i = tg.tabs.length - 1;
17903             
17904             if(this.active && tg.bullets > 0 && i < tg.bullets){
17905                 tg.setActiveBullet(i);
17906             }
17907         }
17908         
17909         this.el.on('click', this.onClick, this);
17910         
17911         if(Roo.isTouch){
17912             this.el.on("touchstart", this.onTouchStart, this);
17913             this.el.on("touchmove", this.onTouchMove, this);
17914             this.el.on("touchend", this.onTouchEnd, this);
17915         }
17916         
17917     },
17918     
17919     onRender : function(ct, position)
17920     {
17921         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17922     },
17923     
17924     setActive : function(state)
17925     {
17926         Roo.log("panel - set active " + this.tabId + "=" + state);
17927         
17928         this.active = state;
17929         if (!state) {
17930             this.el.removeClass('active');
17931             
17932         } else  if (!this.el.hasClass('active')) {
17933             this.el.addClass('active');
17934         }
17935         
17936         this.fireEvent('changed', this, state);
17937     },
17938     
17939     onClick : function(e)
17940     {
17941         e.preventDefault();
17942         
17943         if(!this.href.length){
17944             return;
17945         }
17946         
17947         window.location.href = this.href;
17948     },
17949     
17950     startX : 0,
17951     startY : 0,
17952     endX : 0,
17953     endY : 0,
17954     swiping : false,
17955     
17956     onTouchStart : function(e)
17957     {
17958         this.swiping = false;
17959         
17960         this.startX = e.browserEvent.touches[0].clientX;
17961         this.startY = e.browserEvent.touches[0].clientY;
17962     },
17963     
17964     onTouchMove : function(e)
17965     {
17966         this.swiping = true;
17967         
17968         this.endX = e.browserEvent.touches[0].clientX;
17969         this.endY = e.browserEvent.touches[0].clientY;
17970     },
17971     
17972     onTouchEnd : function(e)
17973     {
17974         if(!this.swiping){
17975             this.onClick(e);
17976             return;
17977         }
17978         
17979         var tabGroup = this.parent();
17980         
17981         if(this.endX > this.startX){ // swiping right
17982             tabGroup.showPanelPrev();
17983             return;
17984         }
17985         
17986         if(this.startX > this.endX){ // swiping left
17987             tabGroup.showPanelNext();
17988             return;
17989         }
17990     }
17991     
17992     
17993 });
17994  
17995
17996  
17997
17998  /*
17999  * - LGPL
18000  *
18001  * DateField
18002  * 
18003  */
18004
18005 /**
18006  * @class Roo.bootstrap.DateField
18007  * @extends Roo.bootstrap.Input
18008  * Bootstrap DateField class
18009  * @cfg {Number} weekStart default 0
18010  * @cfg {String} viewMode default empty, (months|years)
18011  * @cfg {String} minViewMode default empty, (months|years)
18012  * @cfg {Number} startDate default -Infinity
18013  * @cfg {Number} endDate default Infinity
18014  * @cfg {Boolean} todayHighlight default false
18015  * @cfg {Boolean} todayBtn default false
18016  * @cfg {Boolean} calendarWeeks default false
18017  * @cfg {Object} daysOfWeekDisabled default empty
18018  * @cfg {Boolean} singleMode default false (true | false)
18019  * 
18020  * @cfg {Boolean} keyboardNavigation default true
18021  * @cfg {String} language default en
18022  * 
18023  * @constructor
18024  * Create a new DateField
18025  * @param {Object} config The config object
18026  */
18027
18028 Roo.bootstrap.DateField = function(config){
18029     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18030      this.addEvents({
18031             /**
18032              * @event show
18033              * Fires when this field show.
18034              * @param {Roo.bootstrap.DateField} this
18035              * @param {Mixed} date The date value
18036              */
18037             show : true,
18038             /**
18039              * @event show
18040              * Fires when this field hide.
18041              * @param {Roo.bootstrap.DateField} this
18042              * @param {Mixed} date The date value
18043              */
18044             hide : true,
18045             /**
18046              * @event select
18047              * Fires when select a date.
18048              * @param {Roo.bootstrap.DateField} this
18049              * @param {Mixed} date The date value
18050              */
18051             select : true,
18052             /**
18053              * @event beforeselect
18054              * Fires when before select a date.
18055              * @param {Roo.bootstrap.DateField} this
18056              * @param {Mixed} date The date value
18057              */
18058             beforeselect : true
18059         });
18060 };
18061
18062 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18063     
18064     /**
18065      * @cfg {String} format
18066      * The default date format string which can be overriden for localization support.  The format must be
18067      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18068      */
18069     format : "m/d/y",
18070     /**
18071      * @cfg {String} altFormats
18072      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18073      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18074      */
18075     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18076     
18077     weekStart : 0,
18078     
18079     viewMode : '',
18080     
18081     minViewMode : '',
18082     
18083     todayHighlight : false,
18084     
18085     todayBtn: false,
18086     
18087     language: 'en',
18088     
18089     keyboardNavigation: true,
18090     
18091     calendarWeeks: false,
18092     
18093     startDate: -Infinity,
18094     
18095     endDate: Infinity,
18096     
18097     daysOfWeekDisabled: [],
18098     
18099     _events: [],
18100     
18101     singleMode : false,
18102     
18103     UTCDate: function()
18104     {
18105         return new Date(Date.UTC.apply(Date, arguments));
18106     },
18107     
18108     UTCToday: function()
18109     {
18110         var today = new Date();
18111         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18112     },
18113     
18114     getDate: function() {
18115             var d = this.getUTCDate();
18116             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18117     },
18118     
18119     getUTCDate: function() {
18120             return this.date;
18121     },
18122     
18123     setDate: function(d) {
18124             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18125     },
18126     
18127     setUTCDate: function(d) {
18128             this.date = d;
18129             this.setValue(this.formatDate(this.date));
18130     },
18131         
18132     onRender: function(ct, position)
18133     {
18134         
18135         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18136         
18137         this.language = this.language || 'en';
18138         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18139         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18140         
18141         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18142         this.format = this.format || 'm/d/y';
18143         this.isInline = false;
18144         this.isInput = true;
18145         this.component = this.el.select('.add-on', true).first() || false;
18146         this.component = (this.component && this.component.length === 0) ? false : this.component;
18147         this.hasInput = this.component && this.inputEl().length;
18148         
18149         if (typeof(this.minViewMode === 'string')) {
18150             switch (this.minViewMode) {
18151                 case 'months':
18152                     this.minViewMode = 1;
18153                     break;
18154                 case 'years':
18155                     this.minViewMode = 2;
18156                     break;
18157                 default:
18158                     this.minViewMode = 0;
18159                     break;
18160             }
18161         }
18162         
18163         if (typeof(this.viewMode === 'string')) {
18164             switch (this.viewMode) {
18165                 case 'months':
18166                     this.viewMode = 1;
18167                     break;
18168                 case 'years':
18169                     this.viewMode = 2;
18170                     break;
18171                 default:
18172                     this.viewMode = 0;
18173                     break;
18174             }
18175         }
18176                 
18177         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18178         
18179 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18180         
18181         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18182         
18183         this.picker().on('mousedown', this.onMousedown, this);
18184         this.picker().on('click', this.onClick, this);
18185         
18186         this.picker().addClass('datepicker-dropdown');
18187         
18188         this.startViewMode = this.viewMode;
18189         
18190         if(this.singleMode){
18191             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18192                 v.setVisibilityMode(Roo.Element.DISPLAY);
18193                 v.hide();
18194             });
18195             
18196             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18197                 v.setStyle('width', '189px');
18198             });
18199         }
18200         
18201         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18202             if(!this.calendarWeeks){
18203                 v.remove();
18204                 return;
18205             }
18206             
18207             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18208             v.attr('colspan', function(i, val){
18209                 return parseInt(val) + 1;
18210             });
18211         });
18212                         
18213         
18214         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18215         
18216         this.setStartDate(this.startDate);
18217         this.setEndDate(this.endDate);
18218         
18219         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18220         
18221         this.fillDow();
18222         this.fillMonths();
18223         this.update();
18224         this.showMode();
18225         
18226         if(this.isInline) {
18227             this.show();
18228         }
18229     },
18230     
18231     picker : function()
18232     {
18233         return this.pickerEl;
18234 //        return this.el.select('.datepicker', true).first();
18235     },
18236     
18237     fillDow: function()
18238     {
18239         var dowCnt = this.weekStart;
18240         
18241         var dow = {
18242             tag: 'tr',
18243             cn: [
18244                 
18245             ]
18246         };
18247         
18248         if(this.calendarWeeks){
18249             dow.cn.push({
18250                 tag: 'th',
18251                 cls: 'cw',
18252                 html: '&nbsp;'
18253             })
18254         }
18255         
18256         while (dowCnt < this.weekStart + 7) {
18257             dow.cn.push({
18258                 tag: 'th',
18259                 cls: 'dow',
18260                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18261             });
18262         }
18263         
18264         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18265     },
18266     
18267     fillMonths: function()
18268     {    
18269         var i = 0;
18270         var months = this.picker().select('>.datepicker-months td', true).first();
18271         
18272         months.dom.innerHTML = '';
18273         
18274         while (i < 12) {
18275             var month = {
18276                 tag: 'span',
18277                 cls: 'month',
18278                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18279             };
18280             
18281             months.createChild(month);
18282         }
18283         
18284     },
18285     
18286     update: function()
18287     {
18288         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;
18289         
18290         if (this.date < this.startDate) {
18291             this.viewDate = new Date(this.startDate);
18292         } else if (this.date > this.endDate) {
18293             this.viewDate = new Date(this.endDate);
18294         } else {
18295             this.viewDate = new Date(this.date);
18296         }
18297         
18298         this.fill();
18299     },
18300     
18301     fill: function() 
18302     {
18303         var d = new Date(this.viewDate),
18304                 year = d.getUTCFullYear(),
18305                 month = d.getUTCMonth(),
18306                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18307                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18308                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18309                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18310                 currentDate = this.date && this.date.valueOf(),
18311                 today = this.UTCToday();
18312         
18313         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18314         
18315 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18316         
18317 //        this.picker.select('>tfoot th.today').
18318 //                                              .text(dates[this.language].today)
18319 //                                              .toggle(this.todayBtn !== false);
18320     
18321         this.updateNavArrows();
18322         this.fillMonths();
18323                                                 
18324         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18325         
18326         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18327          
18328         prevMonth.setUTCDate(day);
18329         
18330         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18331         
18332         var nextMonth = new Date(prevMonth);
18333         
18334         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18335         
18336         nextMonth = nextMonth.valueOf();
18337         
18338         var fillMonths = false;
18339         
18340         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18341         
18342         while(prevMonth.valueOf() < nextMonth) {
18343             var clsName = '';
18344             
18345             if (prevMonth.getUTCDay() === this.weekStart) {
18346                 if(fillMonths){
18347                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18348                 }
18349                     
18350                 fillMonths = {
18351                     tag: 'tr',
18352                     cn: []
18353                 };
18354                 
18355                 if(this.calendarWeeks){
18356                     // ISO 8601: First week contains first thursday.
18357                     // ISO also states week starts on Monday, but we can be more abstract here.
18358                     var
18359                     // Start of current week: based on weekstart/current date
18360                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18361                     // Thursday of this week
18362                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18363                     // First Thursday of year, year from thursday
18364                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18365                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18366                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18367                     
18368                     fillMonths.cn.push({
18369                         tag: 'td',
18370                         cls: 'cw',
18371                         html: calWeek
18372                     });
18373                 }
18374             }
18375             
18376             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18377                 clsName += ' old';
18378             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18379                 clsName += ' new';
18380             }
18381             if (this.todayHighlight &&
18382                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18383                 prevMonth.getUTCMonth() == today.getMonth() &&
18384                 prevMonth.getUTCDate() == today.getDate()) {
18385                 clsName += ' today';
18386             }
18387             
18388             if (currentDate && prevMonth.valueOf() === currentDate) {
18389                 clsName += ' active';
18390             }
18391             
18392             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18393                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18394                     clsName += ' disabled';
18395             }
18396             
18397             fillMonths.cn.push({
18398                 tag: 'td',
18399                 cls: 'day ' + clsName,
18400                 html: prevMonth.getDate()
18401             });
18402             
18403             prevMonth.setDate(prevMonth.getDate()+1);
18404         }
18405           
18406         var currentYear = this.date && this.date.getUTCFullYear();
18407         var currentMonth = this.date && this.date.getUTCMonth();
18408         
18409         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18410         
18411         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18412             v.removeClass('active');
18413             
18414             if(currentYear === year && k === currentMonth){
18415                 v.addClass('active');
18416             }
18417             
18418             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18419                 v.addClass('disabled');
18420             }
18421             
18422         });
18423         
18424         
18425         year = parseInt(year/10, 10) * 10;
18426         
18427         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18428         
18429         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18430         
18431         year -= 1;
18432         for (var i = -1; i < 11; i++) {
18433             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18434                 tag: 'span',
18435                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18436                 html: year
18437             });
18438             
18439             year += 1;
18440         }
18441     },
18442     
18443     showMode: function(dir) 
18444     {
18445         if (dir) {
18446             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18447         }
18448         
18449         Roo.each(this.picker().select('>div',true).elements, function(v){
18450             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18451             v.hide();
18452         });
18453         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18454     },
18455     
18456     place: function()
18457     {
18458         if(this.isInline) {
18459             return;
18460         }
18461         
18462         this.picker().removeClass(['bottom', 'top']);
18463         
18464         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18465             /*
18466              * place to the top of element!
18467              *
18468              */
18469             
18470             this.picker().addClass('top');
18471             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18472             
18473             return;
18474         }
18475         
18476         this.picker().addClass('bottom');
18477         
18478         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18479     },
18480     
18481     parseDate : function(value)
18482     {
18483         if(!value || value instanceof Date){
18484             return value;
18485         }
18486         var v = Date.parseDate(value, this.format);
18487         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18488             v = Date.parseDate(value, 'Y-m-d');
18489         }
18490         if(!v && this.altFormats){
18491             if(!this.altFormatsArray){
18492                 this.altFormatsArray = this.altFormats.split("|");
18493             }
18494             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18495                 v = Date.parseDate(value, this.altFormatsArray[i]);
18496             }
18497         }
18498         return v;
18499     },
18500     
18501     formatDate : function(date, fmt)
18502     {   
18503         return (!date || !(date instanceof Date)) ?
18504         date : date.dateFormat(fmt || this.format);
18505     },
18506     
18507     onFocus : function()
18508     {
18509         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18510         this.show();
18511     },
18512     
18513     onBlur : function()
18514     {
18515         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18516         
18517         var d = this.inputEl().getValue();
18518         
18519         this.setValue(d);
18520                 
18521         this.hide();
18522     },
18523     
18524     show : function()
18525     {
18526         this.picker().show();
18527         this.update();
18528         this.place();
18529         
18530         this.fireEvent('show', this, this.date);
18531     },
18532     
18533     hide : function()
18534     {
18535         if(this.isInline) {
18536             return;
18537         }
18538         this.picker().hide();
18539         this.viewMode = this.startViewMode;
18540         this.showMode();
18541         
18542         this.fireEvent('hide', this, this.date);
18543         
18544     },
18545     
18546     onMousedown: function(e)
18547     {
18548         e.stopPropagation();
18549         e.preventDefault();
18550     },
18551     
18552     keyup: function(e)
18553     {
18554         Roo.bootstrap.DateField.superclass.keyup.call(this);
18555         this.update();
18556     },
18557
18558     setValue: function(v)
18559     {
18560         if(this.fireEvent('beforeselect', this, v) !== false){
18561             var d = new Date(this.parseDate(v) ).clearTime();
18562         
18563             if(isNaN(d.getTime())){
18564                 this.date = this.viewDate = '';
18565                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18566                 return;
18567             }
18568
18569             v = this.formatDate(d);
18570
18571             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18572
18573             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18574
18575             this.update();
18576
18577             this.fireEvent('select', this, this.date);
18578         }
18579     },
18580     
18581     getValue: function()
18582     {
18583         return this.formatDate(this.date);
18584     },
18585     
18586     fireKey: function(e)
18587     {
18588         if (!this.picker().isVisible()){
18589             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18590                 this.show();
18591             }
18592             return;
18593         }
18594         
18595         var dateChanged = false,
18596         dir, day, month,
18597         newDate, newViewDate;
18598         
18599         switch(e.keyCode){
18600             case 27: // escape
18601                 this.hide();
18602                 e.preventDefault();
18603                 break;
18604             case 37: // left
18605             case 39: // right
18606                 if (!this.keyboardNavigation) {
18607                     break;
18608                 }
18609                 dir = e.keyCode == 37 ? -1 : 1;
18610                 
18611                 if (e.ctrlKey){
18612                     newDate = this.moveYear(this.date, dir);
18613                     newViewDate = this.moveYear(this.viewDate, dir);
18614                 } else if (e.shiftKey){
18615                     newDate = this.moveMonth(this.date, dir);
18616                     newViewDate = this.moveMonth(this.viewDate, dir);
18617                 } else {
18618                     newDate = new Date(this.date);
18619                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18620                     newViewDate = new Date(this.viewDate);
18621                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18622                 }
18623                 if (this.dateWithinRange(newDate)){
18624                     this.date = newDate;
18625                     this.viewDate = newViewDate;
18626                     this.setValue(this.formatDate(this.date));
18627 //                    this.update();
18628                     e.preventDefault();
18629                     dateChanged = true;
18630                 }
18631                 break;
18632             case 38: // up
18633             case 40: // down
18634                 if (!this.keyboardNavigation) {
18635                     break;
18636                 }
18637                 dir = e.keyCode == 38 ? -1 : 1;
18638                 if (e.ctrlKey){
18639                     newDate = this.moveYear(this.date, dir);
18640                     newViewDate = this.moveYear(this.viewDate, dir);
18641                 } else if (e.shiftKey){
18642                     newDate = this.moveMonth(this.date, dir);
18643                     newViewDate = this.moveMonth(this.viewDate, dir);
18644                 } else {
18645                     newDate = new Date(this.date);
18646                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18647                     newViewDate = new Date(this.viewDate);
18648                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18649                 }
18650                 if (this.dateWithinRange(newDate)){
18651                     this.date = newDate;
18652                     this.viewDate = newViewDate;
18653                     this.setValue(this.formatDate(this.date));
18654 //                    this.update();
18655                     e.preventDefault();
18656                     dateChanged = true;
18657                 }
18658                 break;
18659             case 13: // enter
18660                 this.setValue(this.formatDate(this.date));
18661                 this.hide();
18662                 e.preventDefault();
18663                 break;
18664             case 9: // tab
18665                 this.setValue(this.formatDate(this.date));
18666                 this.hide();
18667                 break;
18668             case 16: // shift
18669             case 17: // ctrl
18670             case 18: // alt
18671                 break;
18672             default :
18673                 this.hide();
18674                 
18675         }
18676     },
18677     
18678     
18679     onClick: function(e) 
18680     {
18681         e.stopPropagation();
18682         e.preventDefault();
18683         
18684         var target = e.getTarget();
18685         
18686         if(target.nodeName.toLowerCase() === 'i'){
18687             target = Roo.get(target).dom.parentNode;
18688         }
18689         
18690         var nodeName = target.nodeName;
18691         var className = target.className;
18692         var html = target.innerHTML;
18693         //Roo.log(nodeName);
18694         
18695         switch(nodeName.toLowerCase()) {
18696             case 'th':
18697                 switch(className) {
18698                     case 'switch':
18699                         this.showMode(1);
18700                         break;
18701                     case 'prev':
18702                     case 'next':
18703                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18704                         switch(this.viewMode){
18705                                 case 0:
18706                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18707                                         break;
18708                                 case 1:
18709                                 case 2:
18710                                         this.viewDate = this.moveYear(this.viewDate, dir);
18711                                         break;
18712                         }
18713                         this.fill();
18714                         break;
18715                     case 'today':
18716                         var date = new Date();
18717                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18718 //                        this.fill()
18719                         this.setValue(this.formatDate(this.date));
18720                         
18721                         this.hide();
18722                         break;
18723                 }
18724                 break;
18725             case 'span':
18726                 if (className.indexOf('disabled') < 0) {
18727                     this.viewDate.setUTCDate(1);
18728                     if (className.indexOf('month') > -1) {
18729                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18730                     } else {
18731                         var year = parseInt(html, 10) || 0;
18732                         this.viewDate.setUTCFullYear(year);
18733                         
18734                     }
18735                     
18736                     if(this.singleMode){
18737                         this.setValue(this.formatDate(this.viewDate));
18738                         this.hide();
18739                         return;
18740                     }
18741                     
18742                     this.showMode(-1);
18743                     this.fill();
18744                 }
18745                 break;
18746                 
18747             case 'td':
18748                 //Roo.log(className);
18749                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18750                     var day = parseInt(html, 10) || 1;
18751                     var year = this.viewDate.getUTCFullYear(),
18752                         month = this.viewDate.getUTCMonth();
18753
18754                     if (className.indexOf('old') > -1) {
18755                         if(month === 0 ){
18756                             month = 11;
18757                             year -= 1;
18758                         }else{
18759                             month -= 1;
18760                         }
18761                     } else if (className.indexOf('new') > -1) {
18762                         if (month == 11) {
18763                             month = 0;
18764                             year += 1;
18765                         } else {
18766                             month += 1;
18767                         }
18768                     }
18769                     //Roo.log([year,month,day]);
18770                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18771                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18772 //                    this.fill();
18773                     //Roo.log(this.formatDate(this.date));
18774                     this.setValue(this.formatDate(this.date));
18775                     this.hide();
18776                 }
18777                 break;
18778         }
18779     },
18780     
18781     setStartDate: function(startDate)
18782     {
18783         this.startDate = startDate || -Infinity;
18784         if (this.startDate !== -Infinity) {
18785             this.startDate = this.parseDate(this.startDate);
18786         }
18787         this.update();
18788         this.updateNavArrows();
18789     },
18790
18791     setEndDate: function(endDate)
18792     {
18793         this.endDate = endDate || Infinity;
18794         if (this.endDate !== Infinity) {
18795             this.endDate = this.parseDate(this.endDate);
18796         }
18797         this.update();
18798         this.updateNavArrows();
18799     },
18800     
18801     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18802     {
18803         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18804         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18805             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18806         }
18807         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18808             return parseInt(d, 10);
18809         });
18810         this.update();
18811         this.updateNavArrows();
18812     },
18813     
18814     updateNavArrows: function() 
18815     {
18816         if(this.singleMode){
18817             return;
18818         }
18819         
18820         var d = new Date(this.viewDate),
18821         year = d.getUTCFullYear(),
18822         month = d.getUTCMonth();
18823         
18824         Roo.each(this.picker().select('.prev', true).elements, function(v){
18825             v.show();
18826             switch (this.viewMode) {
18827                 case 0:
18828
18829                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18830                         v.hide();
18831                     }
18832                     break;
18833                 case 1:
18834                 case 2:
18835                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18836                         v.hide();
18837                     }
18838                     break;
18839             }
18840         });
18841         
18842         Roo.each(this.picker().select('.next', true).elements, function(v){
18843             v.show();
18844             switch (this.viewMode) {
18845                 case 0:
18846
18847                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18848                         v.hide();
18849                     }
18850                     break;
18851                 case 1:
18852                 case 2:
18853                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18854                         v.hide();
18855                     }
18856                     break;
18857             }
18858         })
18859     },
18860     
18861     moveMonth: function(date, dir)
18862     {
18863         if (!dir) {
18864             return date;
18865         }
18866         var new_date = new Date(date.valueOf()),
18867         day = new_date.getUTCDate(),
18868         month = new_date.getUTCMonth(),
18869         mag = Math.abs(dir),
18870         new_month, test;
18871         dir = dir > 0 ? 1 : -1;
18872         if (mag == 1){
18873             test = dir == -1
18874             // If going back one month, make sure month is not current month
18875             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18876             ? function(){
18877                 return new_date.getUTCMonth() == month;
18878             }
18879             // If going forward one month, make sure month is as expected
18880             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18881             : function(){
18882                 return new_date.getUTCMonth() != new_month;
18883             };
18884             new_month = month + dir;
18885             new_date.setUTCMonth(new_month);
18886             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18887             if (new_month < 0 || new_month > 11) {
18888                 new_month = (new_month + 12) % 12;
18889             }
18890         } else {
18891             // For magnitudes >1, move one month at a time...
18892             for (var i=0; i<mag; i++) {
18893                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18894                 new_date = this.moveMonth(new_date, dir);
18895             }
18896             // ...then reset the day, keeping it in the new month
18897             new_month = new_date.getUTCMonth();
18898             new_date.setUTCDate(day);
18899             test = function(){
18900                 return new_month != new_date.getUTCMonth();
18901             };
18902         }
18903         // Common date-resetting loop -- if date is beyond end of month, make it
18904         // end of month
18905         while (test()){
18906             new_date.setUTCDate(--day);
18907             new_date.setUTCMonth(new_month);
18908         }
18909         return new_date;
18910     },
18911
18912     moveYear: function(date, dir)
18913     {
18914         return this.moveMonth(date, dir*12);
18915     },
18916
18917     dateWithinRange: function(date)
18918     {
18919         return date >= this.startDate && date <= this.endDate;
18920     },
18921
18922     
18923     remove: function() 
18924     {
18925         this.picker().remove();
18926     },
18927     
18928     validateValue : function(value)
18929     {
18930         if(value.length < 1)  {
18931             if(this.allowBlank){
18932                 return true;
18933             }
18934             return false;
18935         }
18936         
18937         if(value.length < this.minLength){
18938             return false;
18939         }
18940         if(value.length > this.maxLength){
18941             return false;
18942         }
18943         if(this.vtype){
18944             var vt = Roo.form.VTypes;
18945             if(!vt[this.vtype](value, this)){
18946                 return false;
18947             }
18948         }
18949         if(typeof this.validator == "function"){
18950             var msg = this.validator(value);
18951             if(msg !== true){
18952                 return false;
18953             }
18954         }
18955         
18956         if(this.regex && !this.regex.test(value)){
18957             return false;
18958         }
18959         
18960         if(typeof(this.parseDate(value)) == 'undefined'){
18961             return false;
18962         }
18963         
18964         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18965             return false;
18966         }      
18967         
18968         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18969             return false;
18970         } 
18971         
18972         
18973         return true;
18974     }
18975    
18976 });
18977
18978 Roo.apply(Roo.bootstrap.DateField,  {
18979     
18980     head : {
18981         tag: 'thead',
18982         cn: [
18983         {
18984             tag: 'tr',
18985             cn: [
18986             {
18987                 tag: 'th',
18988                 cls: 'prev',
18989                 html: '<i class="fa fa-arrow-left"/>'
18990             },
18991             {
18992                 tag: 'th',
18993                 cls: 'switch',
18994                 colspan: '5'
18995             },
18996             {
18997                 tag: 'th',
18998                 cls: 'next',
18999                 html: '<i class="fa fa-arrow-right"/>'
19000             }
19001
19002             ]
19003         }
19004         ]
19005     },
19006     
19007     content : {
19008         tag: 'tbody',
19009         cn: [
19010         {
19011             tag: 'tr',
19012             cn: [
19013             {
19014                 tag: 'td',
19015                 colspan: '7'
19016             }
19017             ]
19018         }
19019         ]
19020     },
19021     
19022     footer : {
19023         tag: 'tfoot',
19024         cn: [
19025         {
19026             tag: 'tr',
19027             cn: [
19028             {
19029                 tag: 'th',
19030                 colspan: '7',
19031                 cls: 'today'
19032             }
19033                     
19034             ]
19035         }
19036         ]
19037     },
19038     
19039     dates:{
19040         en: {
19041             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19042             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19043             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19044             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19045             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19046             today: "Today"
19047         }
19048     },
19049     
19050     modes: [
19051     {
19052         clsName: 'days',
19053         navFnc: 'Month',
19054         navStep: 1
19055     },
19056     {
19057         clsName: 'months',
19058         navFnc: 'FullYear',
19059         navStep: 1
19060     },
19061     {
19062         clsName: 'years',
19063         navFnc: 'FullYear',
19064         navStep: 10
19065     }]
19066 });
19067
19068 Roo.apply(Roo.bootstrap.DateField,  {
19069   
19070     template : {
19071         tag: 'div',
19072         cls: 'datepicker dropdown-menu roo-dynamic',
19073         cn: [
19074         {
19075             tag: 'div',
19076             cls: 'datepicker-days',
19077             cn: [
19078             {
19079                 tag: 'table',
19080                 cls: 'table-condensed',
19081                 cn:[
19082                 Roo.bootstrap.DateField.head,
19083                 {
19084                     tag: 'tbody'
19085                 },
19086                 Roo.bootstrap.DateField.footer
19087                 ]
19088             }
19089             ]
19090         },
19091         {
19092             tag: 'div',
19093             cls: 'datepicker-months',
19094             cn: [
19095             {
19096                 tag: 'table',
19097                 cls: 'table-condensed',
19098                 cn:[
19099                 Roo.bootstrap.DateField.head,
19100                 Roo.bootstrap.DateField.content,
19101                 Roo.bootstrap.DateField.footer
19102                 ]
19103             }
19104             ]
19105         },
19106         {
19107             tag: 'div',
19108             cls: 'datepicker-years',
19109             cn: [
19110             {
19111                 tag: 'table',
19112                 cls: 'table-condensed',
19113                 cn:[
19114                 Roo.bootstrap.DateField.head,
19115                 Roo.bootstrap.DateField.content,
19116                 Roo.bootstrap.DateField.footer
19117                 ]
19118             }
19119             ]
19120         }
19121         ]
19122     }
19123 });
19124
19125  
19126
19127  /*
19128  * - LGPL
19129  *
19130  * TimeField
19131  * 
19132  */
19133
19134 /**
19135  * @class Roo.bootstrap.TimeField
19136  * @extends Roo.bootstrap.Input
19137  * Bootstrap DateField class
19138  * 
19139  * 
19140  * @constructor
19141  * Create a new TimeField
19142  * @param {Object} config The config object
19143  */
19144
19145 Roo.bootstrap.TimeField = function(config){
19146     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19147     this.addEvents({
19148             /**
19149              * @event show
19150              * Fires when this field show.
19151              * @param {Roo.bootstrap.DateField} thisthis
19152              * @param {Mixed} date The date value
19153              */
19154             show : true,
19155             /**
19156              * @event show
19157              * Fires when this field hide.
19158              * @param {Roo.bootstrap.DateField} this
19159              * @param {Mixed} date The date value
19160              */
19161             hide : true,
19162             /**
19163              * @event select
19164              * Fires when select a date.
19165              * @param {Roo.bootstrap.DateField} this
19166              * @param {Mixed} date The date value
19167              */
19168             select : true
19169         });
19170 };
19171
19172 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19173     
19174     /**
19175      * @cfg {String} format
19176      * The default time format string which can be overriden for localization support.  The format must be
19177      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19178      */
19179     format : "H:i",
19180        
19181     onRender: function(ct, position)
19182     {
19183         
19184         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19185                 
19186         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19187         
19188         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19189         
19190         this.pop = this.picker().select('>.datepicker-time',true).first();
19191         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19192         
19193         this.picker().on('mousedown', this.onMousedown, this);
19194         this.picker().on('click', this.onClick, this);
19195         
19196         this.picker().addClass('datepicker-dropdown');
19197     
19198         this.fillTime();
19199         this.update();
19200             
19201         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19202         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19203         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19204         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19205         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19206         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19207
19208     },
19209     
19210     fireKey: function(e){
19211         if (!this.picker().isVisible()){
19212             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19213                 this.show();
19214             }
19215             return;
19216         }
19217
19218         e.preventDefault();
19219         
19220         switch(e.keyCode){
19221             case 27: // escape
19222                 this.hide();
19223                 break;
19224             case 37: // left
19225             case 39: // right
19226                 this.onTogglePeriod();
19227                 break;
19228             case 38: // up
19229                 this.onIncrementMinutes();
19230                 break;
19231             case 40: // down
19232                 this.onDecrementMinutes();
19233                 break;
19234             case 13: // enter
19235             case 9: // tab
19236                 this.setTime();
19237                 break;
19238         }
19239     },
19240     
19241     onClick: function(e) {
19242         e.stopPropagation();
19243         e.preventDefault();
19244     },
19245     
19246     picker : function()
19247     {
19248         return this.el.select('.datepicker', true).first();
19249     },
19250     
19251     fillTime: function()
19252     {    
19253         var time = this.pop.select('tbody', true).first();
19254         
19255         time.dom.innerHTML = '';
19256         
19257         time.createChild({
19258             tag: 'tr',
19259             cn: [
19260                 {
19261                     tag: 'td',
19262                     cn: [
19263                         {
19264                             tag: 'a',
19265                             href: '#',
19266                             cls: 'btn',
19267                             cn: [
19268                                 {
19269                                     tag: 'span',
19270                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19271                                 }
19272                             ]
19273                         } 
19274                     ]
19275                 },
19276                 {
19277                     tag: 'td',
19278                     cls: 'separator'
19279                 },
19280                 {
19281                     tag: 'td',
19282                     cn: [
19283                         {
19284                             tag: 'a',
19285                             href: '#',
19286                             cls: 'btn',
19287                             cn: [
19288                                 {
19289                                     tag: 'span',
19290                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19291                                 }
19292                             ]
19293                         }
19294                     ]
19295                 },
19296                 {
19297                     tag: 'td',
19298                     cls: 'separator'
19299                 }
19300             ]
19301         });
19302         
19303         time.createChild({
19304             tag: 'tr',
19305             cn: [
19306                 {
19307                     tag: 'td',
19308                     cn: [
19309                         {
19310                             tag: 'span',
19311                             cls: 'timepicker-hour',
19312                             html: '00'
19313                         }  
19314                     ]
19315                 },
19316                 {
19317                     tag: 'td',
19318                     cls: 'separator',
19319                     html: ':'
19320                 },
19321                 {
19322                     tag: 'td',
19323                     cn: [
19324                         {
19325                             tag: 'span',
19326                             cls: 'timepicker-minute',
19327                             html: '00'
19328                         }  
19329                     ]
19330                 },
19331                 {
19332                     tag: 'td',
19333                     cls: 'separator'
19334                 },
19335                 {
19336                     tag: 'td',
19337                     cn: [
19338                         {
19339                             tag: 'button',
19340                             type: 'button',
19341                             cls: 'btn btn-primary period',
19342                             html: 'AM'
19343                             
19344                         }
19345                     ]
19346                 }
19347             ]
19348         });
19349         
19350         time.createChild({
19351             tag: 'tr',
19352             cn: [
19353                 {
19354                     tag: 'td',
19355                     cn: [
19356                         {
19357                             tag: 'a',
19358                             href: '#',
19359                             cls: 'btn',
19360                             cn: [
19361                                 {
19362                                     tag: 'span',
19363                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19364                                 }
19365                             ]
19366                         }
19367                     ]
19368                 },
19369                 {
19370                     tag: 'td',
19371                     cls: 'separator'
19372                 },
19373                 {
19374                     tag: 'td',
19375                     cn: [
19376                         {
19377                             tag: 'a',
19378                             href: '#',
19379                             cls: 'btn',
19380                             cn: [
19381                                 {
19382                                     tag: 'span',
19383                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19384                                 }
19385                             ]
19386                         }
19387                     ]
19388                 },
19389                 {
19390                     tag: 'td',
19391                     cls: 'separator'
19392                 }
19393             ]
19394         });
19395         
19396     },
19397     
19398     update: function()
19399     {
19400         
19401         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19402         
19403         this.fill();
19404     },
19405     
19406     fill: function() 
19407     {
19408         var hours = this.time.getHours();
19409         var minutes = this.time.getMinutes();
19410         var period = 'AM';
19411         
19412         if(hours > 11){
19413             period = 'PM';
19414         }
19415         
19416         if(hours == 0){
19417             hours = 12;
19418         }
19419         
19420         
19421         if(hours > 12){
19422             hours = hours - 12;
19423         }
19424         
19425         if(hours < 10){
19426             hours = '0' + hours;
19427         }
19428         
19429         if(minutes < 10){
19430             minutes = '0' + minutes;
19431         }
19432         
19433         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19434         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19435         this.pop.select('button', true).first().dom.innerHTML = period;
19436         
19437     },
19438     
19439     place: function()
19440     {   
19441         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19442         
19443         var cls = ['bottom'];
19444         
19445         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19446             cls.pop();
19447             cls.push('top');
19448         }
19449         
19450         cls.push('right');
19451         
19452         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19453             cls.pop();
19454             cls.push('left');
19455         }
19456         
19457         this.picker().addClass(cls.join('-'));
19458         
19459         var _this = this;
19460         
19461         Roo.each(cls, function(c){
19462             if(c == 'bottom'){
19463                 _this.picker().setTop(_this.inputEl().getHeight());
19464                 return;
19465             }
19466             if(c == 'top'){
19467                 _this.picker().setTop(0 - _this.picker().getHeight());
19468                 return;
19469             }
19470             
19471             if(c == 'left'){
19472                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19473                 return;
19474             }
19475             if(c == 'right'){
19476                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19477                 return;
19478             }
19479         });
19480         
19481     },
19482   
19483     onFocus : function()
19484     {
19485         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19486         this.show();
19487     },
19488     
19489     onBlur : function()
19490     {
19491         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19492         this.hide();
19493     },
19494     
19495     show : function()
19496     {
19497         this.picker().show();
19498         this.pop.show();
19499         this.update();
19500         this.place();
19501         
19502         this.fireEvent('show', this, this.date);
19503     },
19504     
19505     hide : function()
19506     {
19507         this.picker().hide();
19508         this.pop.hide();
19509         
19510         this.fireEvent('hide', this, this.date);
19511     },
19512     
19513     setTime : function()
19514     {
19515         this.hide();
19516         this.setValue(this.time.format(this.format));
19517         
19518         this.fireEvent('select', this, this.date);
19519         
19520         
19521     },
19522     
19523     onMousedown: function(e){
19524         e.stopPropagation();
19525         e.preventDefault();
19526     },
19527     
19528     onIncrementHours: function()
19529     {
19530         Roo.log('onIncrementHours');
19531         this.time = this.time.add(Date.HOUR, 1);
19532         this.update();
19533         
19534     },
19535     
19536     onDecrementHours: function()
19537     {
19538         Roo.log('onDecrementHours');
19539         this.time = this.time.add(Date.HOUR, -1);
19540         this.update();
19541     },
19542     
19543     onIncrementMinutes: function()
19544     {
19545         Roo.log('onIncrementMinutes');
19546         this.time = this.time.add(Date.MINUTE, 1);
19547         this.update();
19548     },
19549     
19550     onDecrementMinutes: function()
19551     {
19552         Roo.log('onDecrementMinutes');
19553         this.time = this.time.add(Date.MINUTE, -1);
19554         this.update();
19555     },
19556     
19557     onTogglePeriod: function()
19558     {
19559         Roo.log('onTogglePeriod');
19560         this.time = this.time.add(Date.HOUR, 12);
19561         this.update();
19562     }
19563     
19564    
19565 });
19566
19567 Roo.apply(Roo.bootstrap.TimeField,  {
19568     
19569     content : {
19570         tag: 'tbody',
19571         cn: [
19572             {
19573                 tag: 'tr',
19574                 cn: [
19575                 {
19576                     tag: 'td',
19577                     colspan: '7'
19578                 }
19579                 ]
19580             }
19581         ]
19582     },
19583     
19584     footer : {
19585         tag: 'tfoot',
19586         cn: [
19587             {
19588                 tag: 'tr',
19589                 cn: [
19590                 {
19591                     tag: 'th',
19592                     colspan: '7',
19593                     cls: '',
19594                     cn: [
19595                         {
19596                             tag: 'button',
19597                             cls: 'btn btn-info ok',
19598                             html: 'OK'
19599                         }
19600                     ]
19601                 }
19602
19603                 ]
19604             }
19605         ]
19606     }
19607 });
19608
19609 Roo.apply(Roo.bootstrap.TimeField,  {
19610   
19611     template : {
19612         tag: 'div',
19613         cls: 'datepicker dropdown-menu',
19614         cn: [
19615             {
19616                 tag: 'div',
19617                 cls: 'datepicker-time',
19618                 cn: [
19619                 {
19620                     tag: 'table',
19621                     cls: 'table-condensed',
19622                     cn:[
19623                     Roo.bootstrap.TimeField.content,
19624                     Roo.bootstrap.TimeField.footer
19625                     ]
19626                 }
19627                 ]
19628             }
19629         ]
19630     }
19631 });
19632
19633  
19634
19635  /*
19636  * - LGPL
19637  *
19638  * MonthField
19639  * 
19640  */
19641
19642 /**
19643  * @class Roo.bootstrap.MonthField
19644  * @extends Roo.bootstrap.Input
19645  * Bootstrap MonthField class
19646  * 
19647  * @cfg {String} language default en
19648  * 
19649  * @constructor
19650  * Create a new MonthField
19651  * @param {Object} config The config object
19652  */
19653
19654 Roo.bootstrap.MonthField = function(config){
19655     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19656     
19657     this.addEvents({
19658         /**
19659          * @event show
19660          * Fires when this field show.
19661          * @param {Roo.bootstrap.MonthField} this
19662          * @param {Mixed} date The date value
19663          */
19664         show : true,
19665         /**
19666          * @event show
19667          * Fires when this field hide.
19668          * @param {Roo.bootstrap.MonthField} this
19669          * @param {Mixed} date The date value
19670          */
19671         hide : true,
19672         /**
19673          * @event select
19674          * Fires when select a date.
19675          * @param {Roo.bootstrap.MonthField} this
19676          * @param {String} oldvalue The old value
19677          * @param {String} newvalue The new value
19678          */
19679         select : true
19680     });
19681 };
19682
19683 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19684     
19685     onRender: function(ct, position)
19686     {
19687         
19688         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19689         
19690         this.language = this.language || 'en';
19691         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19692         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19693         
19694         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19695         this.isInline = false;
19696         this.isInput = true;
19697         this.component = this.el.select('.add-on', true).first() || false;
19698         this.component = (this.component && this.component.length === 0) ? false : this.component;
19699         this.hasInput = this.component && this.inputEL().length;
19700         
19701         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19702         
19703         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19704         
19705         this.picker().on('mousedown', this.onMousedown, this);
19706         this.picker().on('click', this.onClick, this);
19707         
19708         this.picker().addClass('datepicker-dropdown');
19709         
19710         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19711             v.setStyle('width', '189px');
19712         });
19713         
19714         this.fillMonths();
19715         
19716         this.update();
19717         
19718         if(this.isInline) {
19719             this.show();
19720         }
19721         
19722     },
19723     
19724     setValue: function(v, suppressEvent)
19725     {   
19726         var o = this.getValue();
19727         
19728         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19729         
19730         this.update();
19731
19732         if(suppressEvent !== true){
19733             this.fireEvent('select', this, o, v);
19734         }
19735         
19736     },
19737     
19738     getValue: function()
19739     {
19740         return this.value;
19741     },
19742     
19743     onClick: function(e) 
19744     {
19745         e.stopPropagation();
19746         e.preventDefault();
19747         
19748         var target = e.getTarget();
19749         
19750         if(target.nodeName.toLowerCase() === 'i'){
19751             target = Roo.get(target).dom.parentNode;
19752         }
19753         
19754         var nodeName = target.nodeName;
19755         var className = target.className;
19756         var html = target.innerHTML;
19757         
19758         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19759             return;
19760         }
19761         
19762         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19763         
19764         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19765         
19766         this.hide();
19767                         
19768     },
19769     
19770     picker : function()
19771     {
19772         return this.pickerEl;
19773     },
19774     
19775     fillMonths: function()
19776     {    
19777         var i = 0;
19778         var months = this.picker().select('>.datepicker-months td', true).first();
19779         
19780         months.dom.innerHTML = '';
19781         
19782         while (i < 12) {
19783             var month = {
19784                 tag: 'span',
19785                 cls: 'month',
19786                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19787             };
19788             
19789             months.createChild(month);
19790         }
19791         
19792     },
19793     
19794     update: function()
19795     {
19796         var _this = this;
19797         
19798         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19799             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19800         }
19801         
19802         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19803             e.removeClass('active');
19804             
19805             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19806                 e.addClass('active');
19807             }
19808         })
19809     },
19810     
19811     place: function()
19812     {
19813         if(this.isInline) {
19814             return;
19815         }
19816         
19817         this.picker().removeClass(['bottom', 'top']);
19818         
19819         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19820             /*
19821              * place to the top of element!
19822              *
19823              */
19824             
19825             this.picker().addClass('top');
19826             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19827             
19828             return;
19829         }
19830         
19831         this.picker().addClass('bottom');
19832         
19833         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19834     },
19835     
19836     onFocus : function()
19837     {
19838         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19839         this.show();
19840     },
19841     
19842     onBlur : function()
19843     {
19844         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19845         
19846         var d = this.inputEl().getValue();
19847         
19848         this.setValue(d);
19849                 
19850         this.hide();
19851     },
19852     
19853     show : function()
19854     {
19855         this.picker().show();
19856         this.picker().select('>.datepicker-months', true).first().show();
19857         this.update();
19858         this.place();
19859         
19860         this.fireEvent('show', this, this.date);
19861     },
19862     
19863     hide : function()
19864     {
19865         if(this.isInline) {
19866             return;
19867         }
19868         this.picker().hide();
19869         this.fireEvent('hide', this, this.date);
19870         
19871     },
19872     
19873     onMousedown: function(e)
19874     {
19875         e.stopPropagation();
19876         e.preventDefault();
19877     },
19878     
19879     keyup: function(e)
19880     {
19881         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19882         this.update();
19883     },
19884
19885     fireKey: function(e)
19886     {
19887         if (!this.picker().isVisible()){
19888             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19889                 this.show();
19890             }
19891             return;
19892         }
19893         
19894         var dir;
19895         
19896         switch(e.keyCode){
19897             case 27: // escape
19898                 this.hide();
19899                 e.preventDefault();
19900                 break;
19901             case 37: // left
19902             case 39: // right
19903                 dir = e.keyCode == 37 ? -1 : 1;
19904                 
19905                 this.vIndex = this.vIndex + dir;
19906                 
19907                 if(this.vIndex < 0){
19908                     this.vIndex = 0;
19909                 }
19910                 
19911                 if(this.vIndex > 11){
19912                     this.vIndex = 11;
19913                 }
19914                 
19915                 if(isNaN(this.vIndex)){
19916                     this.vIndex = 0;
19917                 }
19918                 
19919                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19920                 
19921                 break;
19922             case 38: // up
19923             case 40: // down
19924                 
19925                 dir = e.keyCode == 38 ? -1 : 1;
19926                 
19927                 this.vIndex = this.vIndex + dir * 4;
19928                 
19929                 if(this.vIndex < 0){
19930                     this.vIndex = 0;
19931                 }
19932                 
19933                 if(this.vIndex > 11){
19934                     this.vIndex = 11;
19935                 }
19936                 
19937                 if(isNaN(this.vIndex)){
19938                     this.vIndex = 0;
19939                 }
19940                 
19941                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19942                 break;
19943                 
19944             case 13: // enter
19945                 
19946                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19947                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19948                 }
19949                 
19950                 this.hide();
19951                 e.preventDefault();
19952                 break;
19953             case 9: // tab
19954                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19955                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19956                 }
19957                 this.hide();
19958                 break;
19959             case 16: // shift
19960             case 17: // ctrl
19961             case 18: // alt
19962                 break;
19963             default :
19964                 this.hide();
19965                 
19966         }
19967     },
19968     
19969     remove: function() 
19970     {
19971         this.picker().remove();
19972     }
19973    
19974 });
19975
19976 Roo.apply(Roo.bootstrap.MonthField,  {
19977     
19978     content : {
19979         tag: 'tbody',
19980         cn: [
19981         {
19982             tag: 'tr',
19983             cn: [
19984             {
19985                 tag: 'td',
19986                 colspan: '7'
19987             }
19988             ]
19989         }
19990         ]
19991     },
19992     
19993     dates:{
19994         en: {
19995             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19996             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19997         }
19998     }
19999 });
20000
20001 Roo.apply(Roo.bootstrap.MonthField,  {
20002   
20003     template : {
20004         tag: 'div',
20005         cls: 'datepicker dropdown-menu roo-dynamic',
20006         cn: [
20007             {
20008                 tag: 'div',
20009                 cls: 'datepicker-months',
20010                 cn: [
20011                 {
20012                     tag: 'table',
20013                     cls: 'table-condensed',
20014                     cn:[
20015                         Roo.bootstrap.DateField.content
20016                     ]
20017                 }
20018                 ]
20019             }
20020         ]
20021     }
20022 });
20023
20024  
20025
20026  
20027  /*
20028  * - LGPL
20029  *
20030  * CheckBox
20031  * 
20032  */
20033
20034 /**
20035  * @class Roo.bootstrap.CheckBox
20036  * @extends Roo.bootstrap.Input
20037  * Bootstrap CheckBox class
20038  * 
20039  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20040  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20041  * @cfg {String} boxLabel The text that appears beside the checkbox
20042  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20043  * @cfg {Boolean} checked initnal the element
20044  * @cfg {Boolean} inline inline the element (default false)
20045  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20046  * 
20047  * @constructor
20048  * Create a new CheckBox
20049  * @param {Object} config The config object
20050  */
20051
20052 Roo.bootstrap.CheckBox = function(config){
20053     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20054    
20055     this.addEvents({
20056         /**
20057         * @event check
20058         * Fires when the element is checked or unchecked.
20059         * @param {Roo.bootstrap.CheckBox} this This input
20060         * @param {Boolean} checked The new checked value
20061         */
20062        check : true
20063     });
20064     
20065 };
20066
20067 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20068   
20069     inputType: 'checkbox',
20070     inputValue: 1,
20071     valueOff: 0,
20072     boxLabel: false,
20073     checked: false,
20074     weight : false,
20075     inline: false,
20076     
20077     getAutoCreate : function()
20078     {
20079         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20080         
20081         var id = Roo.id();
20082         
20083         var cfg = {};
20084         
20085         cfg.cls = 'form-group ' + this.inputType; //input-group
20086         
20087         if(this.inline){
20088             cfg.cls += ' ' + this.inputType + '-inline';
20089         }
20090         
20091         var input =  {
20092             tag: 'input',
20093             id : id,
20094             type : this.inputType,
20095             value : this.inputValue,
20096             cls : 'roo-' + this.inputType, //'form-box',
20097             placeholder : this.placeholder || ''
20098             
20099         };
20100         
20101         if(this.inputType != 'radio'){
20102             var hidden =  {
20103                 tag: 'input',
20104                 type : 'hidden',
20105                 cls : 'roo-hidden-value',
20106                 value : this.checked ? this.valueOff : this.inputValue
20107             };
20108         }
20109         
20110             
20111         if (this.weight) { // Validity check?
20112             cfg.cls += " " + this.inputType + "-" + this.weight;
20113         }
20114         
20115         if (this.disabled) {
20116             input.disabled=true;
20117         }
20118         
20119         if(this.checked){
20120             input.checked = this.checked;
20121             
20122         }
20123         
20124         
20125         if (this.name) {
20126             
20127             input.name = this.name;
20128             
20129             if(this.inputType != 'radio'){
20130                 hidden.name = this.name;
20131                 input.name = '_hidden_' + this.name;
20132             }
20133         }
20134         
20135         if (this.size) {
20136             input.cls += ' input-' + this.size;
20137         }
20138         
20139         var settings=this;
20140         
20141         ['xs','sm','md','lg'].map(function(size){
20142             if (settings[size]) {
20143                 cfg.cls += ' col-' + size + '-' + settings[size];
20144             }
20145         });
20146         
20147         var inputblock = input;
20148          
20149         if (this.before || this.after) {
20150             
20151             inputblock = {
20152                 cls : 'input-group',
20153                 cn :  [] 
20154             };
20155             
20156             if (this.before) {
20157                 inputblock.cn.push({
20158                     tag :'span',
20159                     cls : 'input-group-addon',
20160                     html : this.before
20161                 });
20162             }
20163             
20164             inputblock.cn.push(input);
20165             
20166             if(this.inputType != 'radio'){
20167                 inputblock.cn.push(hidden);
20168             }
20169             
20170             if (this.after) {
20171                 inputblock.cn.push({
20172                     tag :'span',
20173                     cls : 'input-group-addon',
20174                     html : this.after
20175                 });
20176             }
20177             
20178         }
20179         
20180         if (align ==='left' && this.fieldLabel.length) {
20181 //                Roo.log("left and has label");
20182             cfg.cn = [
20183                 {
20184                     tag: 'label',
20185                     'for' :  id,
20186                     cls : 'control-label',
20187                     html : this.fieldLabel
20188
20189                 },
20190                 {
20191                     cls : "", 
20192                     cn: [
20193                         inputblock
20194                     ]
20195                 }
20196             ];
20197             
20198             if(this.labelWidth > 12){
20199                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20200             }
20201             
20202             if(this.labelWidth < 13 && this.labelmd == 0){
20203                 this.labelmd = this.labelWidth;
20204             }
20205             
20206             if(this.labellg > 0){
20207                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20208                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20209             }
20210             
20211             if(this.labelmd > 0){
20212                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20213                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20214             }
20215             
20216             if(this.labelsm > 0){
20217                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20218                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20219             }
20220             
20221             if(this.labelxs > 0){
20222                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20223                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20224             }
20225             
20226         } else if ( this.fieldLabel.length) {
20227 //                Roo.log(" label");
20228                 cfg.cn = [
20229                    
20230                     {
20231                         tag: this.boxLabel ? 'span' : 'label',
20232                         'for': id,
20233                         cls: 'control-label box-input-label',
20234                         //cls : 'input-group-addon',
20235                         html : this.fieldLabel
20236                         
20237                     },
20238                     
20239                     inputblock
20240                     
20241                 ];
20242
20243         } else {
20244             
20245 //                Roo.log(" no label && no align");
20246                 cfg.cn = [  inputblock ] ;
20247                 
20248                 
20249         }
20250         
20251         if(this.boxLabel){
20252              var boxLabelCfg = {
20253                 tag: 'label',
20254                 //'for': id, // box label is handled by onclick - so no for...
20255                 cls: 'box-label',
20256                 html: this.boxLabel
20257             };
20258             
20259             if(this.tooltip){
20260                 boxLabelCfg.tooltip = this.tooltip;
20261             }
20262              
20263             cfg.cn.push(boxLabelCfg);
20264         }
20265         
20266         if(this.inputType != 'radio'){
20267             cfg.cn.push(hidden);
20268         }
20269         
20270         return cfg;
20271         
20272     },
20273     
20274     /**
20275      * return the real input element.
20276      */
20277     inputEl: function ()
20278     {
20279         return this.el.select('input.roo-' + this.inputType,true).first();
20280     },
20281     hiddenEl: function ()
20282     {
20283         return this.el.select('input.roo-hidden-value',true).first();
20284     },
20285     
20286     labelEl: function()
20287     {
20288         return this.el.select('label.control-label',true).first();
20289     },
20290     /* depricated... */
20291     
20292     label: function()
20293     {
20294         return this.labelEl();
20295     },
20296     
20297     boxLabelEl: function()
20298     {
20299         return this.el.select('label.box-label',true).first();
20300     },
20301     
20302     initEvents : function()
20303     {
20304 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20305         
20306         this.inputEl().on('click', this.onClick,  this);
20307         
20308         if (this.boxLabel) { 
20309             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20310         }
20311         
20312         this.startValue = this.getValue();
20313         
20314         if(this.groupId){
20315             Roo.bootstrap.CheckBox.register(this);
20316         }
20317     },
20318     
20319     onClick : function()
20320     {   
20321         this.setChecked(!this.checked);
20322     },
20323     
20324     setChecked : function(state,suppressEvent)
20325     {
20326         this.startValue = this.getValue();
20327
20328         if(this.inputType == 'radio'){
20329             
20330             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20331                 e.dom.checked = false;
20332             });
20333             
20334             this.inputEl().dom.checked = true;
20335             
20336             this.inputEl().dom.value = this.inputValue;
20337             
20338             if(suppressEvent !== true){
20339                 this.fireEvent('check', this, true);
20340             }
20341             
20342             this.validate();
20343             
20344             return;
20345         }
20346         
20347         this.checked = state;
20348         
20349         this.inputEl().dom.checked = state;
20350         
20351         
20352         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20353         
20354         if(suppressEvent !== true){
20355             this.fireEvent('check', this, state);
20356         }
20357         
20358         this.validate();
20359     },
20360     
20361     getValue : function()
20362     {
20363         if(this.inputType == 'radio'){
20364             return this.getGroupValue();
20365         }
20366         
20367         return this.hiddenEl().dom.value;
20368         
20369     },
20370     
20371     getGroupValue : function()
20372     {
20373         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20374             return '';
20375         }
20376         
20377         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20378     },
20379     
20380     setValue : function(v,suppressEvent)
20381     {
20382         if(this.inputType == 'radio'){
20383             this.setGroupValue(v, suppressEvent);
20384             return;
20385         }
20386         
20387         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20388         
20389         this.validate();
20390     },
20391     
20392     setGroupValue : function(v, suppressEvent)
20393     {
20394         this.startValue = this.getValue();
20395         
20396         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20397             e.dom.checked = false;
20398             
20399             if(e.dom.value == v){
20400                 e.dom.checked = true;
20401             }
20402         });
20403         
20404         if(suppressEvent !== true){
20405             this.fireEvent('check', this, true);
20406         }
20407
20408         this.validate();
20409         
20410         return;
20411     },
20412     
20413     validate : function()
20414     {
20415         if(
20416                 this.disabled || 
20417                 (this.inputType == 'radio' && this.validateRadio()) ||
20418                 (this.inputType == 'checkbox' && this.validateCheckbox())
20419         ){
20420             this.markValid();
20421             return true;
20422         }
20423         
20424         this.markInvalid();
20425         return false;
20426     },
20427     
20428     validateRadio : function()
20429     {
20430         if(this.allowBlank){
20431             return true;
20432         }
20433         
20434         var valid = false;
20435         
20436         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20437             if(!e.dom.checked){
20438                 return;
20439             }
20440             
20441             valid = true;
20442             
20443             return false;
20444         });
20445         
20446         return valid;
20447     },
20448     
20449     validateCheckbox : function()
20450     {
20451         if(!this.groupId){
20452             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20453             //return (this.getValue() == this.inputValue) ? true : false;
20454         }
20455         
20456         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20457         
20458         if(!group){
20459             return false;
20460         }
20461         
20462         var r = false;
20463         
20464         for(var i in group){
20465             if(r){
20466                 break;
20467             }
20468             
20469             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20470         }
20471         
20472         return r;
20473     },
20474     
20475     /**
20476      * Mark this field as valid
20477      */
20478     markValid : function()
20479     {
20480         var _this = this;
20481         
20482         this.fireEvent('valid', this);
20483         
20484         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20485         
20486         if(this.groupId){
20487             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20488         }
20489         
20490         if(label){
20491             label.markValid();
20492         }
20493
20494         if(this.inputType == 'radio'){
20495             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20496                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20497                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20498             });
20499             
20500             return;
20501         }
20502
20503         if(!this.groupId){
20504             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20505             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20506             return;
20507         }
20508         
20509         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20510         
20511         if(!group){
20512             return;
20513         }
20514         
20515         for(var i in group){
20516             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20517             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20518         }
20519     },
20520     
20521      /**
20522      * Mark this field as invalid
20523      * @param {String} msg The validation message
20524      */
20525     markInvalid : function(msg)
20526     {
20527         if(this.allowBlank){
20528             return;
20529         }
20530         
20531         var _this = this;
20532         
20533         this.fireEvent('invalid', this, msg);
20534         
20535         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20536         
20537         if(this.groupId){
20538             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20539         }
20540         
20541         if(label){
20542             label.markInvalid();
20543         }
20544             
20545         if(this.inputType == 'radio'){
20546             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20547                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20548                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20549             });
20550             
20551             return;
20552         }
20553         
20554         if(!this.groupId){
20555             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20556             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20557             return;
20558         }
20559         
20560         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20561         
20562         if(!group){
20563             return;
20564         }
20565         
20566         for(var i in group){
20567             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20568             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20569         }
20570         
20571     },
20572     
20573     clearInvalid : function()
20574     {
20575         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20576         
20577         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20578         
20579         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20580         
20581         if (label) {
20582             label.iconEl.removeClass(label.validClass);
20583             label.iconEl.removeClass(label.invalidClass);
20584         }
20585     },
20586     
20587     disable : function()
20588     {
20589         if(this.inputType != 'radio'){
20590             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20591             return;
20592         }
20593         
20594         var _this = this;
20595         
20596         if(this.rendered){
20597             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20598                 _this.getActionEl().addClass(this.disabledClass);
20599                 e.dom.disabled = true;
20600             });
20601         }
20602         
20603         this.disabled = true;
20604         this.fireEvent("disable", this);
20605         return this;
20606     },
20607
20608     enable : function()
20609     {
20610         if(this.inputType != 'radio'){
20611             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20612             return;
20613         }
20614         
20615         var _this = this;
20616         
20617         if(this.rendered){
20618             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20619                 _this.getActionEl().removeClass(this.disabledClass);
20620                 e.dom.disabled = false;
20621             });
20622         }
20623         
20624         this.disabled = false;
20625         this.fireEvent("enable", this);
20626         return this;
20627     }
20628
20629 });
20630
20631 Roo.apply(Roo.bootstrap.CheckBox, {
20632     
20633     groups: {},
20634     
20635      /**
20636     * register a CheckBox Group
20637     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20638     */
20639     register : function(checkbox)
20640     {
20641         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20642             this.groups[checkbox.groupId] = {};
20643         }
20644         
20645         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20646             return;
20647         }
20648         
20649         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20650         
20651     },
20652     /**
20653     * fetch a CheckBox Group based on the group ID
20654     * @param {string} the group ID
20655     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20656     */
20657     get: function(groupId) {
20658         if (typeof(this.groups[groupId]) == 'undefined') {
20659             return false;
20660         }
20661         
20662         return this.groups[groupId] ;
20663     }
20664     
20665     
20666 });
20667 /*
20668  * - LGPL
20669  *
20670  * RadioItem
20671  * 
20672  */
20673
20674 /**
20675  * @class Roo.bootstrap.Radio
20676  * @extends Roo.bootstrap.Component
20677  * Bootstrap Radio class
20678  * @cfg {String} boxLabel - the label associated
20679  * @cfg {String} value - the value of radio
20680  * 
20681  * @constructor
20682  * Create a new Radio
20683  * @param {Object} config The config object
20684  */
20685 Roo.bootstrap.Radio = function(config){
20686     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20687     
20688 };
20689
20690 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20691     
20692     boxLabel : '',
20693     
20694     value : '',
20695     
20696     getAutoCreate : function()
20697     {
20698         var cfg = {
20699             tag : 'div',
20700             cls : 'form-group radio',
20701             cn : [
20702                 {
20703                     tag : 'label',
20704                     cls : 'box-label',
20705                     html : this.boxLabel
20706                 }
20707             ]
20708         };
20709         
20710         return cfg;
20711     },
20712     
20713     initEvents : function() 
20714     {
20715         this.parent().register(this);
20716         
20717         this.el.on('click', this.onClick, this);
20718         
20719     },
20720     
20721     onClick : function()
20722     {
20723         this.setChecked(true);
20724     },
20725     
20726     setChecked : function(state, suppressEvent)
20727     {
20728         this.parent().setValue(this.value, suppressEvent);
20729         
20730     }
20731     
20732 });
20733  
20734
20735  /*
20736  * - LGPL
20737  *
20738  * Input
20739  * 
20740  */
20741
20742 /**
20743  * @class Roo.bootstrap.SecurePass
20744  * @extends Roo.bootstrap.Input
20745  * Bootstrap SecurePass class
20746  *
20747  * 
20748  * @constructor
20749  * Create a new SecurePass
20750  * @param {Object} config The config object
20751  */
20752  
20753 Roo.bootstrap.SecurePass = function (config) {
20754     // these go here, so the translation tool can replace them..
20755     this.errors = {
20756         PwdEmpty: "Please type a password, and then retype it to confirm.",
20757         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20758         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20759         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20760         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20761         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20762         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20763         TooWeak: "Your password is Too Weak."
20764     },
20765     this.meterLabel = "Password strength:";
20766     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20767     this.meterClass = [
20768         "roo-password-meter-tooweak", 
20769         "roo-password-meter-weak", 
20770         "roo-password-meter-medium", 
20771         "roo-password-meter-strong", 
20772         "roo-password-meter-grey"
20773     ];
20774     
20775     this.errors = {};
20776     
20777     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20778 }
20779
20780 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20781     /**
20782      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20783      * {
20784      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20785      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20786      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20787      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20788      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20789      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20790      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20791      * })
20792      */
20793     // private
20794     
20795     meterWidth: 300,
20796     errorMsg :'',    
20797     errors: false,
20798     imageRoot: '/',
20799     /**
20800      * @cfg {String/Object} Label for the strength meter (defaults to
20801      * 'Password strength:')
20802      */
20803     // private
20804     meterLabel: '',
20805     /**
20806      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20807      * ['Weak', 'Medium', 'Strong'])
20808      */
20809     // private    
20810     pwdStrengths: false,    
20811     // private
20812     strength: 0,
20813     // private
20814     _lastPwd: null,
20815     // private
20816     kCapitalLetter: 0,
20817     kSmallLetter: 1,
20818     kDigit: 2,
20819     kPunctuation: 3,
20820     
20821     insecure: false,
20822     // private
20823     initEvents: function ()
20824     {
20825         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20826
20827         if (this.el.is('input[type=password]') && Roo.isSafari) {
20828             this.el.on('keydown', this.SafariOnKeyDown, this);
20829         }
20830
20831         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20832     },
20833     // private
20834     onRender: function (ct, position)
20835     {
20836         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20837         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20838         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20839
20840         this.trigger.createChild({
20841                    cn: [
20842                     {
20843                     //id: 'PwdMeter',
20844                     tag: 'div',
20845                     cls: 'roo-password-meter-grey col-xs-12',
20846                     style: {
20847                         //width: 0,
20848                         //width: this.meterWidth + 'px'                                                
20849                         }
20850                     },
20851                     {                            
20852                          cls: 'roo-password-meter-text'                          
20853                     }
20854                 ]            
20855         });
20856
20857          
20858         if (this.hideTrigger) {
20859             this.trigger.setDisplayed(false);
20860         }
20861         this.setSize(this.width || '', this.height || '');
20862     },
20863     // private
20864     onDestroy: function ()
20865     {
20866         if (this.trigger) {
20867             this.trigger.removeAllListeners();
20868             this.trigger.remove();
20869         }
20870         if (this.wrap) {
20871             this.wrap.remove();
20872         }
20873         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20874     },
20875     // private
20876     checkStrength: function ()
20877     {
20878         var pwd = this.inputEl().getValue();
20879         if (pwd == this._lastPwd) {
20880             return;
20881         }
20882
20883         var strength;
20884         if (this.ClientSideStrongPassword(pwd)) {
20885             strength = 3;
20886         } else if (this.ClientSideMediumPassword(pwd)) {
20887             strength = 2;
20888         } else if (this.ClientSideWeakPassword(pwd)) {
20889             strength = 1;
20890         } else {
20891             strength = 0;
20892         }
20893         
20894         Roo.log('strength1: ' + strength);
20895         
20896         //var pm = this.trigger.child('div/div/div').dom;
20897         var pm = this.trigger.child('div/div');
20898         pm.removeClass(this.meterClass);
20899         pm.addClass(this.meterClass[strength]);
20900                 
20901         
20902         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20903                 
20904         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20905         
20906         this._lastPwd = pwd;
20907     },
20908     reset: function ()
20909     {
20910         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20911         
20912         this._lastPwd = '';
20913         
20914         var pm = this.trigger.child('div/div');
20915         pm.removeClass(this.meterClass);
20916         pm.addClass('roo-password-meter-grey');        
20917         
20918         
20919         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20920         
20921         pt.innerHTML = '';
20922         this.inputEl().dom.type='password';
20923     },
20924     // private
20925     validateValue: function (value)
20926     {
20927         
20928         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20929             return false;
20930         }
20931         if (value.length == 0) {
20932             if (this.allowBlank) {
20933                 this.clearInvalid();
20934                 return true;
20935             }
20936
20937             this.markInvalid(this.errors.PwdEmpty);
20938             this.errorMsg = this.errors.PwdEmpty;
20939             return false;
20940         }
20941         
20942         if(this.insecure){
20943             return true;
20944         }
20945         
20946         if ('[\x21-\x7e]*'.match(value)) {
20947             this.markInvalid(this.errors.PwdBadChar);
20948             this.errorMsg = this.errors.PwdBadChar;
20949             return false;
20950         }
20951         if (value.length < 6) {
20952             this.markInvalid(this.errors.PwdShort);
20953             this.errorMsg = this.errors.PwdShort;
20954             return false;
20955         }
20956         if (value.length > 16) {
20957             this.markInvalid(this.errors.PwdLong);
20958             this.errorMsg = this.errors.PwdLong;
20959             return false;
20960         }
20961         var strength;
20962         if (this.ClientSideStrongPassword(value)) {
20963             strength = 3;
20964         } else if (this.ClientSideMediumPassword(value)) {
20965             strength = 2;
20966         } else if (this.ClientSideWeakPassword(value)) {
20967             strength = 1;
20968         } else {
20969             strength = 0;
20970         }
20971
20972         
20973         if (strength < 2) {
20974             //this.markInvalid(this.errors.TooWeak);
20975             this.errorMsg = this.errors.TooWeak;
20976             //return false;
20977         }
20978         
20979         
20980         console.log('strength2: ' + strength);
20981         
20982         //var pm = this.trigger.child('div/div/div').dom;
20983         
20984         var pm = this.trigger.child('div/div');
20985         pm.removeClass(this.meterClass);
20986         pm.addClass(this.meterClass[strength]);
20987                 
20988         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20989                 
20990         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20991         
20992         this.errorMsg = ''; 
20993         return true;
20994     },
20995     // private
20996     CharacterSetChecks: function (type)
20997     {
20998         this.type = type;
20999         this.fResult = false;
21000     },
21001     // private
21002     isctype: function (character, type)
21003     {
21004         switch (type) {  
21005             case this.kCapitalLetter:
21006                 if (character >= 'A' && character <= 'Z') {
21007                     return true;
21008                 }
21009                 break;
21010             
21011             case this.kSmallLetter:
21012                 if (character >= 'a' && character <= 'z') {
21013                     return true;
21014                 }
21015                 break;
21016             
21017             case this.kDigit:
21018                 if (character >= '0' && character <= '9') {
21019                     return true;
21020                 }
21021                 break;
21022             
21023             case this.kPunctuation:
21024                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21025                     return true;
21026                 }
21027                 break;
21028             
21029             default:
21030                 return false;
21031         }
21032
21033     },
21034     // private
21035     IsLongEnough: function (pwd, size)
21036     {
21037         return !(pwd == null || isNaN(size) || pwd.length < size);
21038     },
21039     // private
21040     SpansEnoughCharacterSets: function (word, nb)
21041     {
21042         if (!this.IsLongEnough(word, nb))
21043         {
21044             return false;
21045         }
21046
21047         var characterSetChecks = new Array(
21048             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21049             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21050         );
21051         
21052         for (var index = 0; index < word.length; ++index) {
21053             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21054                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21055                     characterSetChecks[nCharSet].fResult = true;
21056                     break;
21057                 }
21058             }
21059         }
21060
21061         var nCharSets = 0;
21062         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21063             if (characterSetChecks[nCharSet].fResult) {
21064                 ++nCharSets;
21065             }
21066         }
21067
21068         if (nCharSets < nb) {
21069             return false;
21070         }
21071         return true;
21072     },
21073     // private
21074     ClientSideStrongPassword: function (pwd)
21075     {
21076         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21077     },
21078     // private
21079     ClientSideMediumPassword: function (pwd)
21080     {
21081         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21082     },
21083     // private
21084     ClientSideWeakPassword: function (pwd)
21085     {
21086         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21087     }
21088           
21089 })//<script type="text/javascript">
21090
21091 /*
21092  * Based  Ext JS Library 1.1.1
21093  * Copyright(c) 2006-2007, Ext JS, LLC.
21094  * LGPL
21095  *
21096  */
21097  
21098 /**
21099  * @class Roo.HtmlEditorCore
21100  * @extends Roo.Component
21101  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21102  *
21103  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21104  */
21105
21106 Roo.HtmlEditorCore = function(config){
21107     
21108     
21109     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21110     
21111     
21112     this.addEvents({
21113         /**
21114          * @event initialize
21115          * Fires when the editor is fully initialized (including the iframe)
21116          * @param {Roo.HtmlEditorCore} this
21117          */
21118         initialize: true,
21119         /**
21120          * @event activate
21121          * Fires when the editor is first receives the focus. Any insertion must wait
21122          * until after this event.
21123          * @param {Roo.HtmlEditorCore} this
21124          */
21125         activate: true,
21126          /**
21127          * @event beforesync
21128          * Fires before the textarea is updated with content from the editor iframe. Return false
21129          * to cancel the sync.
21130          * @param {Roo.HtmlEditorCore} this
21131          * @param {String} html
21132          */
21133         beforesync: true,
21134          /**
21135          * @event beforepush
21136          * Fires before the iframe editor is updated with content from the textarea. Return false
21137          * to cancel the push.
21138          * @param {Roo.HtmlEditorCore} this
21139          * @param {String} html
21140          */
21141         beforepush: true,
21142          /**
21143          * @event sync
21144          * Fires when the textarea is updated with content from the editor iframe.
21145          * @param {Roo.HtmlEditorCore} this
21146          * @param {String} html
21147          */
21148         sync: true,
21149          /**
21150          * @event push
21151          * Fires when the iframe editor is updated with content from the textarea.
21152          * @param {Roo.HtmlEditorCore} this
21153          * @param {String} html
21154          */
21155         push: true,
21156         
21157         /**
21158          * @event editorevent
21159          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21160          * @param {Roo.HtmlEditorCore} this
21161          */
21162         editorevent: true
21163         
21164     });
21165     
21166     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21167     
21168     // defaults : white / black...
21169     this.applyBlacklists();
21170     
21171     
21172     
21173 };
21174
21175
21176 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21177
21178
21179      /**
21180      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21181      */
21182     
21183     owner : false,
21184     
21185      /**
21186      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21187      *                        Roo.resizable.
21188      */
21189     resizable : false,
21190      /**
21191      * @cfg {Number} height (in pixels)
21192      */   
21193     height: 300,
21194    /**
21195      * @cfg {Number} width (in pixels)
21196      */   
21197     width: 500,
21198     
21199     /**
21200      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21201      * 
21202      */
21203     stylesheets: false,
21204     
21205     // id of frame..
21206     frameId: false,
21207     
21208     // private properties
21209     validationEvent : false,
21210     deferHeight: true,
21211     initialized : false,
21212     activated : false,
21213     sourceEditMode : false,
21214     onFocus : Roo.emptyFn,
21215     iframePad:3,
21216     hideMode:'offsets',
21217     
21218     clearUp: true,
21219     
21220     // blacklist + whitelisted elements..
21221     black: false,
21222     white: false,
21223      
21224     
21225
21226     /**
21227      * Protected method that will not generally be called directly. It
21228      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21229      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21230      */
21231     getDocMarkup : function(){
21232         // body styles..
21233         var st = '';
21234         
21235         // inherit styels from page...?? 
21236         if (this.stylesheets === false) {
21237             
21238             Roo.get(document.head).select('style').each(function(node) {
21239                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21240             });
21241             
21242             Roo.get(document.head).select('link').each(function(node) { 
21243                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21244             });
21245             
21246         } else if (!this.stylesheets.length) {
21247                 // simple..
21248                 st = '<style type="text/css">' +
21249                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21250                    '</style>';
21251         } else { 
21252             
21253         }
21254         
21255         st +=  '<style type="text/css">' +
21256             'IMG { cursor: pointer } ' +
21257         '</style>';
21258
21259         
21260         return '<html><head>' + st  +
21261             //<style type="text/css">' +
21262             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21263             //'</style>' +
21264             ' </head><body class="roo-htmleditor-body"></body></html>';
21265     },
21266
21267     // private
21268     onRender : function(ct, position)
21269     {
21270         var _t = this;
21271         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21272         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21273         
21274         
21275         this.el.dom.style.border = '0 none';
21276         this.el.dom.setAttribute('tabIndex', -1);
21277         this.el.addClass('x-hidden hide');
21278         
21279         
21280         
21281         if(Roo.isIE){ // fix IE 1px bogus margin
21282             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21283         }
21284        
21285         
21286         this.frameId = Roo.id();
21287         
21288          
21289         
21290         var iframe = this.owner.wrap.createChild({
21291             tag: 'iframe',
21292             cls: 'form-control', // bootstrap..
21293             id: this.frameId,
21294             name: this.frameId,
21295             frameBorder : 'no',
21296             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21297         }, this.el
21298         );
21299         
21300         
21301         this.iframe = iframe.dom;
21302
21303          this.assignDocWin();
21304         
21305         this.doc.designMode = 'on';
21306        
21307         this.doc.open();
21308         this.doc.write(this.getDocMarkup());
21309         this.doc.close();
21310
21311         
21312         var task = { // must defer to wait for browser to be ready
21313             run : function(){
21314                 //console.log("run task?" + this.doc.readyState);
21315                 this.assignDocWin();
21316                 if(this.doc.body || this.doc.readyState == 'complete'){
21317                     try {
21318                         this.doc.designMode="on";
21319                     } catch (e) {
21320                         return;
21321                     }
21322                     Roo.TaskMgr.stop(task);
21323                     this.initEditor.defer(10, this);
21324                 }
21325             },
21326             interval : 10,
21327             duration: 10000,
21328             scope: this
21329         };
21330         Roo.TaskMgr.start(task);
21331
21332     },
21333
21334     // private
21335     onResize : function(w, h)
21336     {
21337          Roo.log('resize: ' +w + ',' + h );
21338         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21339         if(!this.iframe){
21340             return;
21341         }
21342         if(typeof w == 'number'){
21343             
21344             this.iframe.style.width = w + 'px';
21345         }
21346         if(typeof h == 'number'){
21347             
21348             this.iframe.style.height = h + 'px';
21349             if(this.doc){
21350                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21351             }
21352         }
21353         
21354     },
21355
21356     /**
21357      * Toggles the editor between standard and source edit mode.
21358      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21359      */
21360     toggleSourceEdit : function(sourceEditMode){
21361         
21362         this.sourceEditMode = sourceEditMode === true;
21363         
21364         if(this.sourceEditMode){
21365  
21366             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21367             
21368         }else{
21369             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21370             //this.iframe.className = '';
21371             this.deferFocus();
21372         }
21373         //this.setSize(this.owner.wrap.getSize());
21374         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21375     },
21376
21377     
21378   
21379
21380     /**
21381      * Protected method that will not generally be called directly. If you need/want
21382      * custom HTML cleanup, this is the method you should override.
21383      * @param {String} html The HTML to be cleaned
21384      * return {String} The cleaned HTML
21385      */
21386     cleanHtml : function(html){
21387         html = String(html);
21388         if(html.length > 5){
21389             if(Roo.isSafari){ // strip safari nonsense
21390                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21391             }
21392         }
21393         if(html == '&nbsp;'){
21394             html = '';
21395         }
21396         return html;
21397     },
21398
21399     /**
21400      * HTML Editor -> Textarea
21401      * Protected method that will not generally be called directly. Syncs the contents
21402      * of the editor iframe with the textarea.
21403      */
21404     syncValue : function(){
21405         if(this.initialized){
21406             var bd = (this.doc.body || this.doc.documentElement);
21407             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21408             var html = bd.innerHTML;
21409             if(Roo.isSafari){
21410                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21411                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21412                 if(m && m[1]){
21413                     html = '<div style="'+m[0]+'">' + html + '</div>';
21414                 }
21415             }
21416             html = this.cleanHtml(html);
21417             // fix up the special chars.. normaly like back quotes in word...
21418             // however we do not want to do this with chinese..
21419             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21420                 var cc = b.charCodeAt();
21421                 if (
21422                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21423                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21424                     (cc >= 0xf900 && cc < 0xfb00 )
21425                 ) {
21426                         return b;
21427                 }
21428                 return "&#"+cc+";" 
21429             });
21430             if(this.owner.fireEvent('beforesync', this, html) !== false){
21431                 this.el.dom.value = html;
21432                 this.owner.fireEvent('sync', this, html);
21433             }
21434         }
21435     },
21436
21437     /**
21438      * Protected method that will not generally be called directly. Pushes the value of the textarea
21439      * into the iframe editor.
21440      */
21441     pushValue : function(){
21442         if(this.initialized){
21443             var v = this.el.dom.value.trim();
21444             
21445 //            if(v.length < 1){
21446 //                v = '&#160;';
21447 //            }
21448             
21449             if(this.owner.fireEvent('beforepush', this, v) !== false){
21450                 var d = (this.doc.body || this.doc.documentElement);
21451                 d.innerHTML = v;
21452                 this.cleanUpPaste();
21453                 this.el.dom.value = d.innerHTML;
21454                 this.owner.fireEvent('push', this, v);
21455             }
21456         }
21457     },
21458
21459     // private
21460     deferFocus : function(){
21461         this.focus.defer(10, this);
21462     },
21463
21464     // doc'ed in Field
21465     focus : function(){
21466         if(this.win && !this.sourceEditMode){
21467             this.win.focus();
21468         }else{
21469             this.el.focus();
21470         }
21471     },
21472     
21473     assignDocWin: function()
21474     {
21475         var iframe = this.iframe;
21476         
21477          if(Roo.isIE){
21478             this.doc = iframe.contentWindow.document;
21479             this.win = iframe.contentWindow;
21480         } else {
21481 //            if (!Roo.get(this.frameId)) {
21482 //                return;
21483 //            }
21484 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21485 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21486             
21487             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21488                 return;
21489             }
21490             
21491             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21492             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21493         }
21494     },
21495     
21496     // private
21497     initEditor : function(){
21498         //console.log("INIT EDITOR");
21499         this.assignDocWin();
21500         
21501         
21502         
21503         this.doc.designMode="on";
21504         this.doc.open();
21505         this.doc.write(this.getDocMarkup());
21506         this.doc.close();
21507         
21508         var dbody = (this.doc.body || this.doc.documentElement);
21509         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21510         // this copies styles from the containing element into thsi one..
21511         // not sure why we need all of this..
21512         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21513         
21514         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21515         //ss['background-attachment'] = 'fixed'; // w3c
21516         dbody.bgProperties = 'fixed'; // ie
21517         //Roo.DomHelper.applyStyles(dbody, ss);
21518         Roo.EventManager.on(this.doc, {
21519             //'mousedown': this.onEditorEvent,
21520             'mouseup': this.onEditorEvent,
21521             'dblclick': this.onEditorEvent,
21522             'click': this.onEditorEvent,
21523             'keyup': this.onEditorEvent,
21524             buffer:100,
21525             scope: this
21526         });
21527         if(Roo.isGecko){
21528             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21529         }
21530         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21531             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21532         }
21533         this.initialized = true;
21534
21535         this.owner.fireEvent('initialize', this);
21536         this.pushValue();
21537     },
21538
21539     // private
21540     onDestroy : function(){
21541         
21542         
21543         
21544         if(this.rendered){
21545             
21546             //for (var i =0; i < this.toolbars.length;i++) {
21547             //    // fixme - ask toolbars for heights?
21548             //    this.toolbars[i].onDestroy();
21549            // }
21550             
21551             //this.wrap.dom.innerHTML = '';
21552             //this.wrap.remove();
21553         }
21554     },
21555
21556     // private
21557     onFirstFocus : function(){
21558         
21559         this.assignDocWin();
21560         
21561         
21562         this.activated = true;
21563          
21564     
21565         if(Roo.isGecko){ // prevent silly gecko errors
21566             this.win.focus();
21567             var s = this.win.getSelection();
21568             if(!s.focusNode || s.focusNode.nodeType != 3){
21569                 var r = s.getRangeAt(0);
21570                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21571                 r.collapse(true);
21572                 this.deferFocus();
21573             }
21574             try{
21575                 this.execCmd('useCSS', true);
21576                 this.execCmd('styleWithCSS', false);
21577             }catch(e){}
21578         }
21579         this.owner.fireEvent('activate', this);
21580     },
21581
21582     // private
21583     adjustFont: function(btn){
21584         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21585         //if(Roo.isSafari){ // safari
21586         //    adjust *= 2;
21587        // }
21588         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21589         if(Roo.isSafari){ // safari
21590             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21591             v =  (v < 10) ? 10 : v;
21592             v =  (v > 48) ? 48 : v;
21593             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21594             
21595         }
21596         
21597         
21598         v = Math.max(1, v+adjust);
21599         
21600         this.execCmd('FontSize', v  );
21601     },
21602
21603     onEditorEvent : function(e)
21604     {
21605         this.owner.fireEvent('editorevent', this, e);
21606       //  this.updateToolbar();
21607         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21608     },
21609
21610     insertTag : function(tg)
21611     {
21612         // could be a bit smarter... -> wrap the current selected tRoo..
21613         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21614             
21615             range = this.createRange(this.getSelection());
21616             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21617             wrappingNode.appendChild(range.extractContents());
21618             range.insertNode(wrappingNode);
21619
21620             return;
21621             
21622             
21623             
21624         }
21625         this.execCmd("formatblock",   tg);
21626         
21627     },
21628     
21629     insertText : function(txt)
21630     {
21631         
21632         
21633         var range = this.createRange();
21634         range.deleteContents();
21635                //alert(Sender.getAttribute('label'));
21636                
21637         range.insertNode(this.doc.createTextNode(txt));
21638     } ,
21639     
21640      
21641
21642     /**
21643      * Executes a Midas editor command on the editor document and performs necessary focus and
21644      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21645      * @param {String} cmd The Midas command
21646      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21647      */
21648     relayCmd : function(cmd, value){
21649         this.win.focus();
21650         this.execCmd(cmd, value);
21651         this.owner.fireEvent('editorevent', this);
21652         //this.updateToolbar();
21653         this.owner.deferFocus();
21654     },
21655
21656     /**
21657      * Executes a Midas editor command directly on the editor document.
21658      * For visual commands, you should use {@link #relayCmd} instead.
21659      * <b>This should only be called after the editor is initialized.</b>
21660      * @param {String} cmd The Midas command
21661      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21662      */
21663     execCmd : function(cmd, value){
21664         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21665         this.syncValue();
21666     },
21667  
21668  
21669    
21670     /**
21671      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21672      * to insert tRoo.
21673      * @param {String} text | dom node.. 
21674      */
21675     insertAtCursor : function(text)
21676     {
21677         
21678         if(!this.activated){
21679             return;
21680         }
21681         /*
21682         if(Roo.isIE){
21683             this.win.focus();
21684             var r = this.doc.selection.createRange();
21685             if(r){
21686                 r.collapse(true);
21687                 r.pasteHTML(text);
21688                 this.syncValue();
21689                 this.deferFocus();
21690             
21691             }
21692             return;
21693         }
21694         */
21695         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21696             this.win.focus();
21697             
21698             
21699             // from jquery ui (MIT licenced)
21700             var range, node;
21701             var win = this.win;
21702             
21703             if (win.getSelection && win.getSelection().getRangeAt) {
21704                 range = win.getSelection().getRangeAt(0);
21705                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21706                 range.insertNode(node);
21707             } else if (win.document.selection && win.document.selection.createRange) {
21708                 // no firefox support
21709                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21710                 win.document.selection.createRange().pasteHTML(txt);
21711             } else {
21712                 // no firefox support
21713                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21714                 this.execCmd('InsertHTML', txt);
21715             } 
21716             
21717             this.syncValue();
21718             
21719             this.deferFocus();
21720         }
21721     },
21722  // private
21723     mozKeyPress : function(e){
21724         if(e.ctrlKey){
21725             var c = e.getCharCode(), cmd;
21726           
21727             if(c > 0){
21728                 c = String.fromCharCode(c).toLowerCase();
21729                 switch(c){
21730                     case 'b':
21731                         cmd = 'bold';
21732                         break;
21733                     case 'i':
21734                         cmd = 'italic';
21735                         break;
21736                     
21737                     case 'u':
21738                         cmd = 'underline';
21739                         break;
21740                     
21741                     case 'v':
21742                         this.cleanUpPaste.defer(100, this);
21743                         return;
21744                         
21745                 }
21746                 if(cmd){
21747                     this.win.focus();
21748                     this.execCmd(cmd);
21749                     this.deferFocus();
21750                     e.preventDefault();
21751                 }
21752                 
21753             }
21754         }
21755     },
21756
21757     // private
21758     fixKeys : function(){ // load time branching for fastest keydown performance
21759         if(Roo.isIE){
21760             return function(e){
21761                 var k = e.getKey(), r;
21762                 if(k == e.TAB){
21763                     e.stopEvent();
21764                     r = this.doc.selection.createRange();
21765                     if(r){
21766                         r.collapse(true);
21767                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21768                         this.deferFocus();
21769                     }
21770                     return;
21771                 }
21772                 
21773                 if(k == e.ENTER){
21774                     r = this.doc.selection.createRange();
21775                     if(r){
21776                         var target = r.parentElement();
21777                         if(!target || target.tagName.toLowerCase() != 'li'){
21778                             e.stopEvent();
21779                             r.pasteHTML('<br />');
21780                             r.collapse(false);
21781                             r.select();
21782                         }
21783                     }
21784                 }
21785                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21786                     this.cleanUpPaste.defer(100, this);
21787                     return;
21788                 }
21789                 
21790                 
21791             };
21792         }else if(Roo.isOpera){
21793             return function(e){
21794                 var k = e.getKey();
21795                 if(k == e.TAB){
21796                     e.stopEvent();
21797                     this.win.focus();
21798                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21799                     this.deferFocus();
21800                 }
21801                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21802                     this.cleanUpPaste.defer(100, this);
21803                     return;
21804                 }
21805                 
21806             };
21807         }else if(Roo.isSafari){
21808             return function(e){
21809                 var k = e.getKey();
21810                 
21811                 if(k == e.TAB){
21812                     e.stopEvent();
21813                     this.execCmd('InsertText','\t');
21814                     this.deferFocus();
21815                     return;
21816                 }
21817                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21818                     this.cleanUpPaste.defer(100, this);
21819                     return;
21820                 }
21821                 
21822              };
21823         }
21824     }(),
21825     
21826     getAllAncestors: function()
21827     {
21828         var p = this.getSelectedNode();
21829         var a = [];
21830         if (!p) {
21831             a.push(p); // push blank onto stack..
21832             p = this.getParentElement();
21833         }
21834         
21835         
21836         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21837             a.push(p);
21838             p = p.parentNode;
21839         }
21840         a.push(this.doc.body);
21841         return a;
21842     },
21843     lastSel : false,
21844     lastSelNode : false,
21845     
21846     
21847     getSelection : function() 
21848     {
21849         this.assignDocWin();
21850         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21851     },
21852     
21853     getSelectedNode: function() 
21854     {
21855         // this may only work on Gecko!!!
21856         
21857         // should we cache this!!!!
21858         
21859         
21860         
21861          
21862         var range = this.createRange(this.getSelection()).cloneRange();
21863         
21864         if (Roo.isIE) {
21865             var parent = range.parentElement();
21866             while (true) {
21867                 var testRange = range.duplicate();
21868                 testRange.moveToElementText(parent);
21869                 if (testRange.inRange(range)) {
21870                     break;
21871                 }
21872                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21873                     break;
21874                 }
21875                 parent = parent.parentElement;
21876             }
21877             return parent;
21878         }
21879         
21880         // is ancestor a text element.
21881         var ac =  range.commonAncestorContainer;
21882         if (ac.nodeType == 3) {
21883             ac = ac.parentNode;
21884         }
21885         
21886         var ar = ac.childNodes;
21887          
21888         var nodes = [];
21889         var other_nodes = [];
21890         var has_other_nodes = false;
21891         for (var i=0;i<ar.length;i++) {
21892             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21893                 continue;
21894             }
21895             // fullly contained node.
21896             
21897             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21898                 nodes.push(ar[i]);
21899                 continue;
21900             }
21901             
21902             // probably selected..
21903             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21904                 other_nodes.push(ar[i]);
21905                 continue;
21906             }
21907             // outer..
21908             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21909                 continue;
21910             }
21911             
21912             
21913             has_other_nodes = true;
21914         }
21915         if (!nodes.length && other_nodes.length) {
21916             nodes= other_nodes;
21917         }
21918         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21919             return false;
21920         }
21921         
21922         return nodes[0];
21923     },
21924     createRange: function(sel)
21925     {
21926         // this has strange effects when using with 
21927         // top toolbar - not sure if it's a great idea.
21928         //this.editor.contentWindow.focus();
21929         if (typeof sel != "undefined") {
21930             try {
21931                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21932             } catch(e) {
21933                 return this.doc.createRange();
21934             }
21935         } else {
21936             return this.doc.createRange();
21937         }
21938     },
21939     getParentElement: function()
21940     {
21941         
21942         this.assignDocWin();
21943         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21944         
21945         var range = this.createRange(sel);
21946          
21947         try {
21948             var p = range.commonAncestorContainer;
21949             while (p.nodeType == 3) { // text node
21950                 p = p.parentNode;
21951             }
21952             return p;
21953         } catch (e) {
21954             return null;
21955         }
21956     
21957     },
21958     /***
21959      *
21960      * Range intersection.. the hard stuff...
21961      *  '-1' = before
21962      *  '0' = hits..
21963      *  '1' = after.
21964      *         [ -- selected range --- ]
21965      *   [fail]                        [fail]
21966      *
21967      *    basically..
21968      *      if end is before start or  hits it. fail.
21969      *      if start is after end or hits it fail.
21970      *
21971      *   if either hits (but other is outside. - then it's not 
21972      *   
21973      *    
21974      **/
21975     
21976     
21977     // @see http://www.thismuchiknow.co.uk/?p=64.
21978     rangeIntersectsNode : function(range, node)
21979     {
21980         var nodeRange = node.ownerDocument.createRange();
21981         try {
21982             nodeRange.selectNode(node);
21983         } catch (e) {
21984             nodeRange.selectNodeContents(node);
21985         }
21986     
21987         var rangeStartRange = range.cloneRange();
21988         rangeStartRange.collapse(true);
21989     
21990         var rangeEndRange = range.cloneRange();
21991         rangeEndRange.collapse(false);
21992     
21993         var nodeStartRange = nodeRange.cloneRange();
21994         nodeStartRange.collapse(true);
21995     
21996         var nodeEndRange = nodeRange.cloneRange();
21997         nodeEndRange.collapse(false);
21998     
21999         return rangeStartRange.compareBoundaryPoints(
22000                  Range.START_TO_START, nodeEndRange) == -1 &&
22001                rangeEndRange.compareBoundaryPoints(
22002                  Range.START_TO_START, nodeStartRange) == 1;
22003         
22004          
22005     },
22006     rangeCompareNode : function(range, node)
22007     {
22008         var nodeRange = node.ownerDocument.createRange();
22009         try {
22010             nodeRange.selectNode(node);
22011         } catch (e) {
22012             nodeRange.selectNodeContents(node);
22013         }
22014         
22015         
22016         range.collapse(true);
22017     
22018         nodeRange.collapse(true);
22019      
22020         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22021         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22022          
22023         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22024         
22025         var nodeIsBefore   =  ss == 1;
22026         var nodeIsAfter    = ee == -1;
22027         
22028         if (nodeIsBefore && nodeIsAfter) {
22029             return 0; // outer
22030         }
22031         if (!nodeIsBefore && nodeIsAfter) {
22032             return 1; //right trailed.
22033         }
22034         
22035         if (nodeIsBefore && !nodeIsAfter) {
22036             return 2;  // left trailed.
22037         }
22038         // fully contined.
22039         return 3;
22040     },
22041
22042     // private? - in a new class?
22043     cleanUpPaste :  function()
22044     {
22045         // cleans up the whole document..
22046         Roo.log('cleanuppaste');
22047         
22048         this.cleanUpChildren(this.doc.body);
22049         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22050         if (clean != this.doc.body.innerHTML) {
22051             this.doc.body.innerHTML = clean;
22052         }
22053         
22054     },
22055     
22056     cleanWordChars : function(input) {// change the chars to hex code
22057         var he = Roo.HtmlEditorCore;
22058         
22059         var output = input;
22060         Roo.each(he.swapCodes, function(sw) { 
22061             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22062             
22063             output = output.replace(swapper, sw[1]);
22064         });
22065         
22066         return output;
22067     },
22068     
22069     
22070     cleanUpChildren : function (n)
22071     {
22072         if (!n.childNodes.length) {
22073             return;
22074         }
22075         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22076            this.cleanUpChild(n.childNodes[i]);
22077         }
22078     },
22079     
22080     
22081         
22082     
22083     cleanUpChild : function (node)
22084     {
22085         var ed = this;
22086         //console.log(node);
22087         if (node.nodeName == "#text") {
22088             // clean up silly Windows -- stuff?
22089             return; 
22090         }
22091         if (node.nodeName == "#comment") {
22092             node.parentNode.removeChild(node);
22093             // clean up silly Windows -- stuff?
22094             return; 
22095         }
22096         var lcname = node.tagName.toLowerCase();
22097         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22098         // whitelist of tags..
22099         
22100         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22101             // remove node.
22102             node.parentNode.removeChild(node);
22103             return;
22104             
22105         }
22106         
22107         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22108         
22109         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22110         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22111         
22112         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22113         //    remove_keep_children = true;
22114         //}
22115         
22116         if (remove_keep_children) {
22117             this.cleanUpChildren(node);
22118             // inserts everything just before this node...
22119             while (node.childNodes.length) {
22120                 var cn = node.childNodes[0];
22121                 node.removeChild(cn);
22122                 node.parentNode.insertBefore(cn, node);
22123             }
22124             node.parentNode.removeChild(node);
22125             return;
22126         }
22127         
22128         if (!node.attributes || !node.attributes.length) {
22129             this.cleanUpChildren(node);
22130             return;
22131         }
22132         
22133         function cleanAttr(n,v)
22134         {
22135             
22136             if (v.match(/^\./) || v.match(/^\//)) {
22137                 return;
22138             }
22139             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22140                 return;
22141             }
22142             if (v.match(/^#/)) {
22143                 return;
22144             }
22145 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22146             node.removeAttribute(n);
22147             
22148         }
22149         
22150         var cwhite = this.cwhite;
22151         var cblack = this.cblack;
22152             
22153         function cleanStyle(n,v)
22154         {
22155             if (v.match(/expression/)) { //XSS?? should we even bother..
22156                 node.removeAttribute(n);
22157                 return;
22158             }
22159             
22160             var parts = v.split(/;/);
22161             var clean = [];
22162             
22163             Roo.each(parts, function(p) {
22164                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22165                 if (!p.length) {
22166                     return true;
22167                 }
22168                 var l = p.split(':').shift().replace(/\s+/g,'');
22169                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22170                 
22171                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22172 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22173                     //node.removeAttribute(n);
22174                     return true;
22175                 }
22176                 //Roo.log()
22177                 // only allow 'c whitelisted system attributes'
22178                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22179 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22180                     //node.removeAttribute(n);
22181                     return true;
22182                 }
22183                 
22184                 
22185                  
22186                 
22187                 clean.push(p);
22188                 return true;
22189             });
22190             if (clean.length) { 
22191                 node.setAttribute(n, clean.join(';'));
22192             } else {
22193                 node.removeAttribute(n);
22194             }
22195             
22196         }
22197         
22198         
22199         for (var i = node.attributes.length-1; i > -1 ; i--) {
22200             var a = node.attributes[i];
22201             //console.log(a);
22202             
22203             if (a.name.toLowerCase().substr(0,2)=='on')  {
22204                 node.removeAttribute(a.name);
22205                 continue;
22206             }
22207             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22208                 node.removeAttribute(a.name);
22209                 continue;
22210             }
22211             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22212                 cleanAttr(a.name,a.value); // fixme..
22213                 continue;
22214             }
22215             if (a.name == 'style') {
22216                 cleanStyle(a.name,a.value);
22217                 continue;
22218             }
22219             /// clean up MS crap..
22220             // tecnically this should be a list of valid class'es..
22221             
22222             
22223             if (a.name == 'class') {
22224                 if (a.value.match(/^Mso/)) {
22225                     node.className = '';
22226                 }
22227                 
22228                 if (a.value.match(/^body$/)) {
22229                     node.className = '';
22230                 }
22231                 continue;
22232             }
22233             
22234             // style cleanup!?
22235             // class cleanup?
22236             
22237         }
22238         
22239         
22240         this.cleanUpChildren(node);
22241         
22242         
22243     },
22244     
22245     /**
22246      * Clean up MS wordisms...
22247      */
22248     cleanWord : function(node)
22249     {
22250         
22251         
22252         if (!node) {
22253             this.cleanWord(this.doc.body);
22254             return;
22255         }
22256         if (node.nodeName == "#text") {
22257             // clean up silly Windows -- stuff?
22258             return; 
22259         }
22260         if (node.nodeName == "#comment") {
22261             node.parentNode.removeChild(node);
22262             // clean up silly Windows -- stuff?
22263             return; 
22264         }
22265         
22266         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22267             node.parentNode.removeChild(node);
22268             return;
22269         }
22270         
22271         // remove - but keep children..
22272         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22273             while (node.childNodes.length) {
22274                 var cn = node.childNodes[0];
22275                 node.removeChild(cn);
22276                 node.parentNode.insertBefore(cn, node);
22277             }
22278             node.parentNode.removeChild(node);
22279             this.iterateChildren(node, this.cleanWord);
22280             return;
22281         }
22282         // clean styles
22283         if (node.className.length) {
22284             
22285             var cn = node.className.split(/\W+/);
22286             var cna = [];
22287             Roo.each(cn, function(cls) {
22288                 if (cls.match(/Mso[a-zA-Z]+/)) {
22289                     return;
22290                 }
22291                 cna.push(cls);
22292             });
22293             node.className = cna.length ? cna.join(' ') : '';
22294             if (!cna.length) {
22295                 node.removeAttribute("class");
22296             }
22297         }
22298         
22299         if (node.hasAttribute("lang")) {
22300             node.removeAttribute("lang");
22301         }
22302         
22303         if (node.hasAttribute("style")) {
22304             
22305             var styles = node.getAttribute("style").split(";");
22306             var nstyle = [];
22307             Roo.each(styles, function(s) {
22308                 if (!s.match(/:/)) {
22309                     return;
22310                 }
22311                 var kv = s.split(":");
22312                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22313                     return;
22314                 }
22315                 // what ever is left... we allow.
22316                 nstyle.push(s);
22317             });
22318             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22319             if (!nstyle.length) {
22320                 node.removeAttribute('style');
22321             }
22322         }
22323         this.iterateChildren(node, this.cleanWord);
22324         
22325         
22326         
22327     },
22328     /**
22329      * iterateChildren of a Node, calling fn each time, using this as the scole..
22330      * @param {DomNode} node node to iterate children of.
22331      * @param {Function} fn method of this class to call on each item.
22332      */
22333     iterateChildren : function(node, fn)
22334     {
22335         if (!node.childNodes.length) {
22336                 return;
22337         }
22338         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22339            fn.call(this, node.childNodes[i])
22340         }
22341     },
22342     
22343     
22344     /**
22345      * cleanTableWidths.
22346      *
22347      * Quite often pasting from word etc.. results in tables with column and widths.
22348      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22349      *
22350      */
22351     cleanTableWidths : function(node)
22352     {
22353          
22354          
22355         if (!node) {
22356             this.cleanTableWidths(this.doc.body);
22357             return;
22358         }
22359         
22360         // ignore list...
22361         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22362             return; 
22363         }
22364         Roo.log(node.tagName);
22365         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22366             this.iterateChildren(node, this.cleanTableWidths);
22367             return;
22368         }
22369         if (node.hasAttribute('width')) {
22370             node.removeAttribute('width');
22371         }
22372         
22373          
22374         if (node.hasAttribute("style")) {
22375             // pretty basic...
22376             
22377             var styles = node.getAttribute("style").split(";");
22378             var nstyle = [];
22379             Roo.each(styles, function(s) {
22380                 if (!s.match(/:/)) {
22381                     return;
22382                 }
22383                 var kv = s.split(":");
22384                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22385                     return;
22386                 }
22387                 // what ever is left... we allow.
22388                 nstyle.push(s);
22389             });
22390             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22391             if (!nstyle.length) {
22392                 node.removeAttribute('style');
22393             }
22394         }
22395         
22396         this.iterateChildren(node, this.cleanTableWidths);
22397         
22398         
22399     },
22400     
22401     
22402     
22403     
22404     domToHTML : function(currentElement, depth, nopadtext) {
22405         
22406         depth = depth || 0;
22407         nopadtext = nopadtext || false;
22408     
22409         if (!currentElement) {
22410             return this.domToHTML(this.doc.body);
22411         }
22412         
22413         //Roo.log(currentElement);
22414         var j;
22415         var allText = false;
22416         var nodeName = currentElement.nodeName;
22417         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22418         
22419         if  (nodeName == '#text') {
22420             
22421             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22422         }
22423         
22424         
22425         var ret = '';
22426         if (nodeName != 'BODY') {
22427              
22428             var i = 0;
22429             // Prints the node tagName, such as <A>, <IMG>, etc
22430             if (tagName) {
22431                 var attr = [];
22432                 for(i = 0; i < currentElement.attributes.length;i++) {
22433                     // quoting?
22434                     var aname = currentElement.attributes.item(i).name;
22435                     if (!currentElement.attributes.item(i).value.length) {
22436                         continue;
22437                     }
22438                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22439                 }
22440                 
22441                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22442             } 
22443             else {
22444                 
22445                 // eack
22446             }
22447         } else {
22448             tagName = false;
22449         }
22450         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22451             return ret;
22452         }
22453         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22454             nopadtext = true;
22455         }
22456         
22457         
22458         // Traverse the tree
22459         i = 0;
22460         var currentElementChild = currentElement.childNodes.item(i);
22461         var allText = true;
22462         var innerHTML  = '';
22463         lastnode = '';
22464         while (currentElementChild) {
22465             // Formatting code (indent the tree so it looks nice on the screen)
22466             var nopad = nopadtext;
22467             if (lastnode == 'SPAN') {
22468                 nopad  = true;
22469             }
22470             // text
22471             if  (currentElementChild.nodeName == '#text') {
22472                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22473                 toadd = nopadtext ? toadd : toadd.trim();
22474                 if (!nopad && toadd.length > 80) {
22475                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22476                 }
22477                 innerHTML  += toadd;
22478                 
22479                 i++;
22480                 currentElementChild = currentElement.childNodes.item(i);
22481                 lastNode = '';
22482                 continue;
22483             }
22484             allText = false;
22485             
22486             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22487                 
22488             // Recursively traverse the tree structure of the child node
22489             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22490             lastnode = currentElementChild.nodeName;
22491             i++;
22492             currentElementChild=currentElement.childNodes.item(i);
22493         }
22494         
22495         ret += innerHTML;
22496         
22497         if (!allText) {
22498                 // The remaining code is mostly for formatting the tree
22499             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22500         }
22501         
22502         
22503         if (tagName) {
22504             ret+= "</"+tagName+">";
22505         }
22506         return ret;
22507         
22508     },
22509         
22510     applyBlacklists : function()
22511     {
22512         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22513         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22514         
22515         this.white = [];
22516         this.black = [];
22517         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22518             if (b.indexOf(tag) > -1) {
22519                 return;
22520             }
22521             this.white.push(tag);
22522             
22523         }, this);
22524         
22525         Roo.each(w, function(tag) {
22526             if (b.indexOf(tag) > -1) {
22527                 return;
22528             }
22529             if (this.white.indexOf(tag) > -1) {
22530                 return;
22531             }
22532             this.white.push(tag);
22533             
22534         }, this);
22535         
22536         
22537         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22538             if (w.indexOf(tag) > -1) {
22539                 return;
22540             }
22541             this.black.push(tag);
22542             
22543         }, this);
22544         
22545         Roo.each(b, function(tag) {
22546             if (w.indexOf(tag) > -1) {
22547                 return;
22548             }
22549             if (this.black.indexOf(tag) > -1) {
22550                 return;
22551             }
22552             this.black.push(tag);
22553             
22554         }, this);
22555         
22556         
22557         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22558         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22559         
22560         this.cwhite = [];
22561         this.cblack = [];
22562         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22563             if (b.indexOf(tag) > -1) {
22564                 return;
22565             }
22566             this.cwhite.push(tag);
22567             
22568         }, this);
22569         
22570         Roo.each(w, function(tag) {
22571             if (b.indexOf(tag) > -1) {
22572                 return;
22573             }
22574             if (this.cwhite.indexOf(tag) > -1) {
22575                 return;
22576             }
22577             this.cwhite.push(tag);
22578             
22579         }, this);
22580         
22581         
22582         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22583             if (w.indexOf(tag) > -1) {
22584                 return;
22585             }
22586             this.cblack.push(tag);
22587             
22588         }, this);
22589         
22590         Roo.each(b, function(tag) {
22591             if (w.indexOf(tag) > -1) {
22592                 return;
22593             }
22594             if (this.cblack.indexOf(tag) > -1) {
22595                 return;
22596             }
22597             this.cblack.push(tag);
22598             
22599         }, this);
22600     },
22601     
22602     setStylesheets : function(stylesheets)
22603     {
22604         if(typeof(stylesheets) == 'string'){
22605             Roo.get(this.iframe.contentDocument.head).createChild({
22606                 tag : 'link',
22607                 rel : 'stylesheet',
22608                 type : 'text/css',
22609                 href : stylesheets
22610             });
22611             
22612             return;
22613         }
22614         var _this = this;
22615      
22616         Roo.each(stylesheets, function(s) {
22617             if(!s.length){
22618                 return;
22619             }
22620             
22621             Roo.get(_this.iframe.contentDocument.head).createChild({
22622                 tag : 'link',
22623                 rel : 'stylesheet',
22624                 type : 'text/css',
22625                 href : s
22626             });
22627         });
22628
22629         
22630     },
22631     
22632     removeStylesheets : function()
22633     {
22634         var _this = this;
22635         
22636         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22637             s.remove();
22638         });
22639     }
22640     
22641     // hide stuff that is not compatible
22642     /**
22643      * @event blur
22644      * @hide
22645      */
22646     /**
22647      * @event change
22648      * @hide
22649      */
22650     /**
22651      * @event focus
22652      * @hide
22653      */
22654     /**
22655      * @event specialkey
22656      * @hide
22657      */
22658     /**
22659      * @cfg {String} fieldClass @hide
22660      */
22661     /**
22662      * @cfg {String} focusClass @hide
22663      */
22664     /**
22665      * @cfg {String} autoCreate @hide
22666      */
22667     /**
22668      * @cfg {String} inputType @hide
22669      */
22670     /**
22671      * @cfg {String} invalidClass @hide
22672      */
22673     /**
22674      * @cfg {String} invalidText @hide
22675      */
22676     /**
22677      * @cfg {String} msgFx @hide
22678      */
22679     /**
22680      * @cfg {String} validateOnBlur @hide
22681      */
22682 });
22683
22684 Roo.HtmlEditorCore.white = [
22685         'area', 'br', 'img', 'input', 'hr', 'wbr',
22686         
22687        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22688        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22689        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22690        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22691        'table',   'ul',         'xmp', 
22692        
22693        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22694       'thead',   'tr', 
22695      
22696       'dir', 'menu', 'ol', 'ul', 'dl',
22697        
22698       'embed',  'object'
22699 ];
22700
22701
22702 Roo.HtmlEditorCore.black = [
22703     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22704         'applet', // 
22705         'base',   'basefont', 'bgsound', 'blink',  'body', 
22706         'frame',  'frameset', 'head',    'html',   'ilayer', 
22707         'iframe', 'layer',  'link',     'meta',    'object',   
22708         'script', 'style' ,'title',  'xml' // clean later..
22709 ];
22710 Roo.HtmlEditorCore.clean = [
22711     'script', 'style', 'title', 'xml'
22712 ];
22713 Roo.HtmlEditorCore.remove = [
22714     'font'
22715 ];
22716 // attributes..
22717
22718 Roo.HtmlEditorCore.ablack = [
22719     'on'
22720 ];
22721     
22722 Roo.HtmlEditorCore.aclean = [ 
22723     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22724 ];
22725
22726 // protocols..
22727 Roo.HtmlEditorCore.pwhite= [
22728         'http',  'https',  'mailto'
22729 ];
22730
22731 // white listed style attributes.
22732 Roo.HtmlEditorCore.cwhite= [
22733       //  'text-align', /// default is to allow most things..
22734       
22735          
22736 //        'font-size'//??
22737 ];
22738
22739 // black listed style attributes.
22740 Roo.HtmlEditorCore.cblack= [
22741       //  'font-size' -- this can be set by the project 
22742 ];
22743
22744
22745 Roo.HtmlEditorCore.swapCodes   =[ 
22746     [    8211, "--" ], 
22747     [    8212, "--" ], 
22748     [    8216,  "'" ],  
22749     [    8217, "'" ],  
22750     [    8220, '"' ],  
22751     [    8221, '"' ],  
22752     [    8226, "*" ],  
22753     [    8230, "..." ]
22754 ]; 
22755
22756     /*
22757  * - LGPL
22758  *
22759  * HtmlEditor
22760  * 
22761  */
22762
22763 /**
22764  * @class Roo.bootstrap.HtmlEditor
22765  * @extends Roo.bootstrap.TextArea
22766  * Bootstrap HtmlEditor class
22767
22768  * @constructor
22769  * Create a new HtmlEditor
22770  * @param {Object} config The config object
22771  */
22772
22773 Roo.bootstrap.HtmlEditor = function(config){
22774     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22775     if (!this.toolbars) {
22776         this.toolbars = [];
22777     }
22778     
22779     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22780     this.addEvents({
22781             /**
22782              * @event initialize
22783              * Fires when the editor is fully initialized (including the iframe)
22784              * @param {HtmlEditor} this
22785              */
22786             initialize: true,
22787             /**
22788              * @event activate
22789              * Fires when the editor is first receives the focus. Any insertion must wait
22790              * until after this event.
22791              * @param {HtmlEditor} this
22792              */
22793             activate: true,
22794              /**
22795              * @event beforesync
22796              * Fires before the textarea is updated with content from the editor iframe. Return false
22797              * to cancel the sync.
22798              * @param {HtmlEditor} this
22799              * @param {String} html
22800              */
22801             beforesync: true,
22802              /**
22803              * @event beforepush
22804              * Fires before the iframe editor is updated with content from the textarea. Return false
22805              * to cancel the push.
22806              * @param {HtmlEditor} this
22807              * @param {String} html
22808              */
22809             beforepush: true,
22810              /**
22811              * @event sync
22812              * Fires when the textarea is updated with content from the editor iframe.
22813              * @param {HtmlEditor} this
22814              * @param {String} html
22815              */
22816             sync: true,
22817              /**
22818              * @event push
22819              * Fires when the iframe editor is updated with content from the textarea.
22820              * @param {HtmlEditor} this
22821              * @param {String} html
22822              */
22823             push: true,
22824              /**
22825              * @event editmodechange
22826              * Fires when the editor switches edit modes
22827              * @param {HtmlEditor} this
22828              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22829              */
22830             editmodechange: true,
22831             /**
22832              * @event editorevent
22833              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22834              * @param {HtmlEditor} this
22835              */
22836             editorevent: true,
22837             /**
22838              * @event firstfocus
22839              * Fires when on first focus - needed by toolbars..
22840              * @param {HtmlEditor} this
22841              */
22842             firstfocus: true,
22843             /**
22844              * @event autosave
22845              * Auto save the htmlEditor value as a file into Events
22846              * @param {HtmlEditor} this
22847              */
22848             autosave: true,
22849             /**
22850              * @event savedpreview
22851              * preview the saved version of htmlEditor
22852              * @param {HtmlEditor} this
22853              */
22854             savedpreview: true
22855         });
22856 };
22857
22858
22859 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22860     
22861     
22862       /**
22863      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22864      */
22865     toolbars : false,
22866     
22867      /**
22868     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22869     */
22870     btns : [],
22871    
22872      /**
22873      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22874      *                        Roo.resizable.
22875      */
22876     resizable : false,
22877      /**
22878      * @cfg {Number} height (in pixels)
22879      */   
22880     height: 300,
22881    /**
22882      * @cfg {Number} width (in pixels)
22883      */   
22884     width: false,
22885     
22886     /**
22887      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22888      * 
22889      */
22890     stylesheets: false,
22891     
22892     // id of frame..
22893     frameId: false,
22894     
22895     // private properties
22896     validationEvent : false,
22897     deferHeight: true,
22898     initialized : false,
22899     activated : false,
22900     
22901     onFocus : Roo.emptyFn,
22902     iframePad:3,
22903     hideMode:'offsets',
22904     
22905     tbContainer : false,
22906     
22907     toolbarContainer :function() {
22908         return this.wrap.select('.x-html-editor-tb',true).first();
22909     },
22910
22911     /**
22912      * Protected method that will not generally be called directly. It
22913      * is called when the editor creates its toolbar. Override this method if you need to
22914      * add custom toolbar buttons.
22915      * @param {HtmlEditor} editor
22916      */
22917     createToolbar : function(){
22918         Roo.log('renewing');
22919         Roo.log("create toolbars");
22920         
22921         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22922         this.toolbars[0].render(this.toolbarContainer());
22923         
22924         return;
22925         
22926 //        if (!editor.toolbars || !editor.toolbars.length) {
22927 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22928 //        }
22929 //        
22930 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22931 //            editor.toolbars[i] = Roo.factory(
22932 //                    typeof(editor.toolbars[i]) == 'string' ?
22933 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22934 //                Roo.bootstrap.HtmlEditor);
22935 //            editor.toolbars[i].init(editor);
22936 //        }
22937     },
22938
22939      
22940     // private
22941     onRender : function(ct, position)
22942     {
22943        // Roo.log("Call onRender: " + this.xtype);
22944         var _t = this;
22945         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22946       
22947         this.wrap = this.inputEl().wrap({
22948             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22949         });
22950         
22951         this.editorcore.onRender(ct, position);
22952          
22953         if (this.resizable) {
22954             this.resizeEl = new Roo.Resizable(this.wrap, {
22955                 pinned : true,
22956                 wrap: true,
22957                 dynamic : true,
22958                 minHeight : this.height,
22959                 height: this.height,
22960                 handles : this.resizable,
22961                 width: this.width,
22962                 listeners : {
22963                     resize : function(r, w, h) {
22964                         _t.onResize(w,h); // -something
22965                     }
22966                 }
22967             });
22968             
22969         }
22970         this.createToolbar(this);
22971        
22972         
22973         if(!this.width && this.resizable){
22974             this.setSize(this.wrap.getSize());
22975         }
22976         if (this.resizeEl) {
22977             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22978             // should trigger onReize..
22979         }
22980         
22981     },
22982
22983     // private
22984     onResize : function(w, h)
22985     {
22986         Roo.log('resize: ' +w + ',' + h );
22987         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22988         var ew = false;
22989         var eh = false;
22990         
22991         if(this.inputEl() ){
22992             if(typeof w == 'number'){
22993                 var aw = w - this.wrap.getFrameWidth('lr');
22994                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22995                 ew = aw;
22996             }
22997             if(typeof h == 'number'){
22998                  var tbh = -11;  // fixme it needs to tool bar size!
22999                 for (var i =0; i < this.toolbars.length;i++) {
23000                     // fixme - ask toolbars for heights?
23001                     tbh += this.toolbars[i].el.getHeight();
23002                     //if (this.toolbars[i].footer) {
23003                     //    tbh += this.toolbars[i].footer.el.getHeight();
23004                     //}
23005                 }
23006               
23007                 
23008                 
23009                 
23010                 
23011                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23012                 ah -= 5; // knock a few pixes off for look..
23013                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23014                 var eh = ah;
23015             }
23016         }
23017         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23018         this.editorcore.onResize(ew,eh);
23019         
23020     },
23021
23022     /**
23023      * Toggles the editor between standard and source edit mode.
23024      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23025      */
23026     toggleSourceEdit : function(sourceEditMode)
23027     {
23028         this.editorcore.toggleSourceEdit(sourceEditMode);
23029         
23030         if(this.editorcore.sourceEditMode){
23031             Roo.log('editor - showing textarea');
23032             
23033 //            Roo.log('in');
23034 //            Roo.log(this.syncValue());
23035             this.syncValue();
23036             this.inputEl().removeClass(['hide', 'x-hidden']);
23037             this.inputEl().dom.removeAttribute('tabIndex');
23038             this.inputEl().focus();
23039         }else{
23040             Roo.log('editor - hiding textarea');
23041 //            Roo.log('out')
23042 //            Roo.log(this.pushValue()); 
23043             this.pushValue();
23044             
23045             this.inputEl().addClass(['hide', 'x-hidden']);
23046             this.inputEl().dom.setAttribute('tabIndex', -1);
23047             //this.deferFocus();
23048         }
23049          
23050         if(this.resizable){
23051             this.setSize(this.wrap.getSize());
23052         }
23053         
23054         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23055     },
23056  
23057     // private (for BoxComponent)
23058     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23059
23060     // private (for BoxComponent)
23061     getResizeEl : function(){
23062         return this.wrap;
23063     },
23064
23065     // private (for BoxComponent)
23066     getPositionEl : function(){
23067         return this.wrap;
23068     },
23069
23070     // private
23071     initEvents : function(){
23072         this.originalValue = this.getValue();
23073     },
23074
23075 //    /**
23076 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23077 //     * @method
23078 //     */
23079 //    markInvalid : Roo.emptyFn,
23080 //    /**
23081 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23082 //     * @method
23083 //     */
23084 //    clearInvalid : Roo.emptyFn,
23085
23086     setValue : function(v){
23087         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23088         this.editorcore.pushValue();
23089     },
23090
23091      
23092     // private
23093     deferFocus : function(){
23094         this.focus.defer(10, this);
23095     },
23096
23097     // doc'ed in Field
23098     focus : function(){
23099         this.editorcore.focus();
23100         
23101     },
23102       
23103
23104     // private
23105     onDestroy : function(){
23106         
23107         
23108         
23109         if(this.rendered){
23110             
23111             for (var i =0; i < this.toolbars.length;i++) {
23112                 // fixme - ask toolbars for heights?
23113                 this.toolbars[i].onDestroy();
23114             }
23115             
23116             this.wrap.dom.innerHTML = '';
23117             this.wrap.remove();
23118         }
23119     },
23120
23121     // private
23122     onFirstFocus : function(){
23123         //Roo.log("onFirstFocus");
23124         this.editorcore.onFirstFocus();
23125          for (var i =0; i < this.toolbars.length;i++) {
23126             this.toolbars[i].onFirstFocus();
23127         }
23128         
23129     },
23130     
23131     // private
23132     syncValue : function()
23133     {   
23134         this.editorcore.syncValue();
23135     },
23136     
23137     pushValue : function()
23138     {   
23139         this.editorcore.pushValue();
23140     }
23141      
23142     
23143     // hide stuff that is not compatible
23144     /**
23145      * @event blur
23146      * @hide
23147      */
23148     /**
23149      * @event change
23150      * @hide
23151      */
23152     /**
23153      * @event focus
23154      * @hide
23155      */
23156     /**
23157      * @event specialkey
23158      * @hide
23159      */
23160     /**
23161      * @cfg {String} fieldClass @hide
23162      */
23163     /**
23164      * @cfg {String} focusClass @hide
23165      */
23166     /**
23167      * @cfg {String} autoCreate @hide
23168      */
23169     /**
23170      * @cfg {String} inputType @hide
23171      */
23172     /**
23173      * @cfg {String} invalidClass @hide
23174      */
23175     /**
23176      * @cfg {String} invalidText @hide
23177      */
23178     /**
23179      * @cfg {String} msgFx @hide
23180      */
23181     /**
23182      * @cfg {String} validateOnBlur @hide
23183      */
23184 });
23185  
23186     
23187    
23188    
23189    
23190       
23191 Roo.namespace('Roo.bootstrap.htmleditor');
23192 /**
23193  * @class Roo.bootstrap.HtmlEditorToolbar1
23194  * Basic Toolbar
23195  * 
23196  * Usage:
23197  *
23198  new Roo.bootstrap.HtmlEditor({
23199     ....
23200     toolbars : [
23201         new Roo.bootstrap.HtmlEditorToolbar1({
23202             disable : { fonts: 1 , format: 1, ..., ... , ...],
23203             btns : [ .... ]
23204         })
23205     }
23206      
23207  * 
23208  * @cfg {Object} disable List of elements to disable..
23209  * @cfg {Array} btns List of additional buttons.
23210  * 
23211  * 
23212  * NEEDS Extra CSS? 
23213  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23214  */
23215  
23216 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23217 {
23218     
23219     Roo.apply(this, config);
23220     
23221     // default disabled, based on 'good practice'..
23222     this.disable = this.disable || {};
23223     Roo.applyIf(this.disable, {
23224         fontSize : true,
23225         colors : true,
23226         specialElements : true
23227     });
23228     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23229     
23230     this.editor = config.editor;
23231     this.editorcore = config.editor.editorcore;
23232     
23233     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23234     
23235     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23236     // dont call parent... till later.
23237 }
23238 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23239      
23240     bar : true,
23241     
23242     editor : false,
23243     editorcore : false,
23244     
23245     
23246     formats : [
23247         "p" ,  
23248         "h1","h2","h3","h4","h5","h6", 
23249         "pre", "code", 
23250         "abbr", "acronym", "address", "cite", "samp", "var",
23251         'div','span'
23252     ],
23253     
23254     onRender : function(ct, position)
23255     {
23256        // Roo.log("Call onRender: " + this.xtype);
23257         
23258        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23259        Roo.log(this.el);
23260        this.el.dom.style.marginBottom = '0';
23261        var _this = this;
23262        var editorcore = this.editorcore;
23263        var editor= this.editor;
23264        
23265        var children = [];
23266        var btn = function(id,cmd , toggle, handler, html){
23267        
23268             var  event = toggle ? 'toggle' : 'click';
23269        
23270             var a = {
23271                 size : 'sm',
23272                 xtype: 'Button',
23273                 xns: Roo.bootstrap,
23274                 glyphicon : id,
23275                 cmd : id || cmd,
23276                 enableToggle:toggle !== false,
23277                 html : html || '',
23278                 pressed : toggle ? false : null,
23279                 listeners : {}
23280             };
23281             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23282                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23283             };
23284             children.push(a);
23285             return a;
23286        }
23287        
23288     //    var cb_box = function...
23289         
23290         var style = {
23291                 xtype: 'Button',
23292                 size : 'sm',
23293                 xns: Roo.bootstrap,
23294                 glyphicon : 'font',
23295                 //html : 'submit'
23296                 menu : {
23297                     xtype: 'Menu',
23298                     xns: Roo.bootstrap,
23299                     items:  []
23300                 }
23301         };
23302         Roo.each(this.formats, function(f) {
23303             style.menu.items.push({
23304                 xtype :'MenuItem',
23305                 xns: Roo.bootstrap,
23306                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23307                 tagname : f,
23308                 listeners : {
23309                     click : function()
23310                     {
23311                         editorcore.insertTag(this.tagname);
23312                         editor.focus();
23313                     }
23314                 }
23315                 
23316             });
23317         });
23318         children.push(style);   
23319         
23320         btn('bold',false,true);
23321         btn('italic',false,true);
23322         btn('align-left', 'justifyleft',true);
23323         btn('align-center', 'justifycenter',true);
23324         btn('align-right' , 'justifyright',true);
23325         btn('link', false, false, function(btn) {
23326             //Roo.log("create link?");
23327             var url = prompt(this.createLinkText, this.defaultLinkValue);
23328             if(url && url != 'http:/'+'/'){
23329                 this.editorcore.relayCmd('createlink', url);
23330             }
23331         }),
23332         btn('list','insertunorderedlist',true);
23333         btn('pencil', false,true, function(btn){
23334                 Roo.log(this);
23335                 this.toggleSourceEdit(btn.pressed);
23336         });
23337         
23338         if (this.editor.btns.length > 0) {
23339             for (var i = 0; i<this.editor.btns.length; i++) {
23340                 children.push(this.editor.btns[i]);
23341             }
23342         }
23343         
23344         /*
23345         var cog = {
23346                 xtype: 'Button',
23347                 size : 'sm',
23348                 xns: Roo.bootstrap,
23349                 glyphicon : 'cog',
23350                 //html : 'submit'
23351                 menu : {
23352                     xtype: 'Menu',
23353                     xns: Roo.bootstrap,
23354                     items:  []
23355                 }
23356         };
23357         
23358         cog.menu.items.push({
23359             xtype :'MenuItem',
23360             xns: Roo.bootstrap,
23361             html : Clean styles,
23362             tagname : f,
23363             listeners : {
23364                 click : function()
23365                 {
23366                     editorcore.insertTag(this.tagname);
23367                     editor.focus();
23368                 }
23369             }
23370             
23371         });
23372        */
23373         
23374          
23375        this.xtype = 'NavSimplebar';
23376         
23377         for(var i=0;i< children.length;i++) {
23378             
23379             this.buttons.add(this.addxtypeChild(children[i]));
23380             
23381         }
23382         
23383         editor.on('editorevent', this.updateToolbar, this);
23384     },
23385     onBtnClick : function(id)
23386     {
23387        this.editorcore.relayCmd(id);
23388        this.editorcore.focus();
23389     },
23390     
23391     /**
23392      * Protected method that will not generally be called directly. It triggers
23393      * a toolbar update by reading the markup state of the current selection in the editor.
23394      */
23395     updateToolbar: function(){
23396
23397         if(!this.editorcore.activated){
23398             this.editor.onFirstFocus(); // is this neeed?
23399             return;
23400         }
23401
23402         var btns = this.buttons; 
23403         var doc = this.editorcore.doc;
23404         btns.get('bold').setActive(doc.queryCommandState('bold'));
23405         btns.get('italic').setActive(doc.queryCommandState('italic'));
23406         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23407         
23408         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23409         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23410         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23411         
23412         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23413         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23414          /*
23415         
23416         var ans = this.editorcore.getAllAncestors();
23417         if (this.formatCombo) {
23418             
23419             
23420             var store = this.formatCombo.store;
23421             this.formatCombo.setValue("");
23422             for (var i =0; i < ans.length;i++) {
23423                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23424                     // select it..
23425                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23426                     break;
23427                 }
23428             }
23429         }
23430         
23431         
23432         
23433         // hides menus... - so this cant be on a menu...
23434         Roo.bootstrap.MenuMgr.hideAll();
23435         */
23436         Roo.bootstrap.MenuMgr.hideAll();
23437         //this.editorsyncValue();
23438     },
23439     onFirstFocus: function() {
23440         this.buttons.each(function(item){
23441            item.enable();
23442         });
23443     },
23444     toggleSourceEdit : function(sourceEditMode){
23445         
23446           
23447         if(sourceEditMode){
23448             Roo.log("disabling buttons");
23449            this.buttons.each( function(item){
23450                 if(item.cmd != 'pencil'){
23451                     item.disable();
23452                 }
23453             });
23454           
23455         }else{
23456             Roo.log("enabling buttons");
23457             if(this.editorcore.initialized){
23458                 this.buttons.each( function(item){
23459                     item.enable();
23460                 });
23461             }
23462             
23463         }
23464         Roo.log("calling toggole on editor");
23465         // tell the editor that it's been pressed..
23466         this.editor.toggleSourceEdit(sourceEditMode);
23467        
23468     }
23469 });
23470
23471
23472
23473
23474
23475 /**
23476  * @class Roo.bootstrap.Table.AbstractSelectionModel
23477  * @extends Roo.util.Observable
23478  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23479  * implemented by descendant classes.  This class should not be directly instantiated.
23480  * @constructor
23481  */
23482 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23483     this.locked = false;
23484     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23485 };
23486
23487
23488 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23489     /** @ignore Called by the grid automatically. Do not call directly. */
23490     init : function(grid){
23491         this.grid = grid;
23492         this.initEvents();
23493     },
23494
23495     /**
23496      * Locks the selections.
23497      */
23498     lock : function(){
23499         this.locked = true;
23500     },
23501
23502     /**
23503      * Unlocks the selections.
23504      */
23505     unlock : function(){
23506         this.locked = false;
23507     },
23508
23509     /**
23510      * Returns true if the selections are locked.
23511      * @return {Boolean}
23512      */
23513     isLocked : function(){
23514         return this.locked;
23515     }
23516 });
23517 /**
23518  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23519  * @class Roo.bootstrap.Table.RowSelectionModel
23520  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23521  * It supports multiple selections and keyboard selection/navigation. 
23522  * @constructor
23523  * @param {Object} config
23524  */
23525
23526 Roo.bootstrap.Table.RowSelectionModel = function(config){
23527     Roo.apply(this, config);
23528     this.selections = new Roo.util.MixedCollection(false, function(o){
23529         return o.id;
23530     });
23531
23532     this.last = false;
23533     this.lastActive = false;
23534
23535     this.addEvents({
23536         /**
23537              * @event selectionchange
23538              * Fires when the selection changes
23539              * @param {SelectionModel} this
23540              */
23541             "selectionchange" : true,
23542         /**
23543              * @event afterselectionchange
23544              * Fires after the selection changes (eg. by key press or clicking)
23545              * @param {SelectionModel} this
23546              */
23547             "afterselectionchange" : true,
23548         /**
23549              * @event beforerowselect
23550              * Fires when a row is selected being selected, return false to cancel.
23551              * @param {SelectionModel} this
23552              * @param {Number} rowIndex The selected index
23553              * @param {Boolean} keepExisting False if other selections will be cleared
23554              */
23555             "beforerowselect" : true,
23556         /**
23557              * @event rowselect
23558              * Fires when a row is selected.
23559              * @param {SelectionModel} this
23560              * @param {Number} rowIndex The selected index
23561              * @param {Roo.data.Record} r The record
23562              */
23563             "rowselect" : true,
23564         /**
23565              * @event rowdeselect
23566              * Fires when a row is deselected.
23567              * @param {SelectionModel} this
23568              * @param {Number} rowIndex The selected index
23569              */
23570         "rowdeselect" : true
23571     });
23572     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23573     this.locked = false;
23574  };
23575
23576 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23577     /**
23578      * @cfg {Boolean} singleSelect
23579      * True to allow selection of only one row at a time (defaults to false)
23580      */
23581     singleSelect : false,
23582
23583     // private
23584     initEvents : function()
23585     {
23586
23587         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23588         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23589         //}else{ // allow click to work like normal
23590          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23591         //}
23592         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23593         this.grid.on("rowclick", this.handleMouseDown, this);
23594         
23595         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23596             "up" : function(e){
23597                 if(!e.shiftKey){
23598                     this.selectPrevious(e.shiftKey);
23599                 }else if(this.last !== false && this.lastActive !== false){
23600                     var last = this.last;
23601                     this.selectRange(this.last,  this.lastActive-1);
23602                     this.grid.getView().focusRow(this.lastActive);
23603                     if(last !== false){
23604                         this.last = last;
23605                     }
23606                 }else{
23607                     this.selectFirstRow();
23608                 }
23609                 this.fireEvent("afterselectionchange", this);
23610             },
23611             "down" : function(e){
23612                 if(!e.shiftKey){
23613                     this.selectNext(e.shiftKey);
23614                 }else if(this.last !== false && this.lastActive !== false){
23615                     var last = this.last;
23616                     this.selectRange(this.last,  this.lastActive+1);
23617                     this.grid.getView().focusRow(this.lastActive);
23618                     if(last !== false){
23619                         this.last = last;
23620                     }
23621                 }else{
23622                     this.selectFirstRow();
23623                 }
23624                 this.fireEvent("afterselectionchange", this);
23625             },
23626             scope: this
23627         });
23628         this.grid.store.on('load', function(){
23629             this.selections.clear();
23630         },this);
23631         /*
23632         var view = this.grid.view;
23633         view.on("refresh", this.onRefresh, this);
23634         view.on("rowupdated", this.onRowUpdated, this);
23635         view.on("rowremoved", this.onRemove, this);
23636         */
23637     },
23638
23639     // private
23640     onRefresh : function()
23641     {
23642         var ds = this.grid.store, i, v = this.grid.view;
23643         var s = this.selections;
23644         s.each(function(r){
23645             if((i = ds.indexOfId(r.id)) != -1){
23646                 v.onRowSelect(i);
23647             }else{
23648                 s.remove(r);
23649             }
23650         });
23651     },
23652
23653     // private
23654     onRemove : function(v, index, r){
23655         this.selections.remove(r);
23656     },
23657
23658     // private
23659     onRowUpdated : function(v, index, r){
23660         if(this.isSelected(r)){
23661             v.onRowSelect(index);
23662         }
23663     },
23664
23665     /**
23666      * Select records.
23667      * @param {Array} records The records to select
23668      * @param {Boolean} keepExisting (optional) True to keep existing selections
23669      */
23670     selectRecords : function(records, keepExisting)
23671     {
23672         if(!keepExisting){
23673             this.clearSelections();
23674         }
23675             var ds = this.grid.store;
23676         for(var i = 0, len = records.length; i < len; i++){
23677             this.selectRow(ds.indexOf(records[i]), true);
23678         }
23679     },
23680
23681     /**
23682      * Gets the number of selected rows.
23683      * @return {Number}
23684      */
23685     getCount : function(){
23686         return this.selections.length;
23687     },
23688
23689     /**
23690      * Selects the first row in the grid.
23691      */
23692     selectFirstRow : function(){
23693         this.selectRow(0);
23694     },
23695
23696     /**
23697      * Select the last row.
23698      * @param {Boolean} keepExisting (optional) True to keep existing selections
23699      */
23700     selectLastRow : function(keepExisting){
23701         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23702         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23703     },
23704
23705     /**
23706      * Selects the row immediately following the last selected row.
23707      * @param {Boolean} keepExisting (optional) True to keep existing selections
23708      */
23709     selectNext : function(keepExisting)
23710     {
23711             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23712             this.selectRow(this.last+1, keepExisting);
23713             this.grid.getView().focusRow(this.last);
23714         }
23715     },
23716
23717     /**
23718      * Selects the row that precedes the last selected row.
23719      * @param {Boolean} keepExisting (optional) True to keep existing selections
23720      */
23721     selectPrevious : function(keepExisting){
23722         if(this.last){
23723             this.selectRow(this.last-1, keepExisting);
23724             this.grid.getView().focusRow(this.last);
23725         }
23726     },
23727
23728     /**
23729      * Returns the selected records
23730      * @return {Array} Array of selected records
23731      */
23732     getSelections : function(){
23733         return [].concat(this.selections.items);
23734     },
23735
23736     /**
23737      * Returns the first selected record.
23738      * @return {Record}
23739      */
23740     getSelected : function(){
23741         return this.selections.itemAt(0);
23742     },
23743
23744
23745     /**
23746      * Clears all selections.
23747      */
23748     clearSelections : function(fast)
23749     {
23750         if(this.locked) {
23751             return;
23752         }
23753         if(fast !== true){
23754                 var ds = this.grid.store;
23755             var s = this.selections;
23756             s.each(function(r){
23757                 this.deselectRow(ds.indexOfId(r.id));
23758             }, this);
23759             s.clear();
23760         }else{
23761             this.selections.clear();
23762         }
23763         this.last = false;
23764     },
23765
23766
23767     /**
23768      * Selects all rows.
23769      */
23770     selectAll : function(){
23771         if(this.locked) {
23772             return;
23773         }
23774         this.selections.clear();
23775         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23776             this.selectRow(i, true);
23777         }
23778     },
23779
23780     /**
23781      * Returns True if there is a selection.
23782      * @return {Boolean}
23783      */
23784     hasSelection : function(){
23785         return this.selections.length > 0;
23786     },
23787
23788     /**
23789      * Returns True if the specified row is selected.
23790      * @param {Number/Record} record The record or index of the record to check
23791      * @return {Boolean}
23792      */
23793     isSelected : function(index){
23794             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23795         return (r && this.selections.key(r.id) ? true : false);
23796     },
23797
23798     /**
23799      * Returns True if the specified record id is selected.
23800      * @param {String} id The id of record to check
23801      * @return {Boolean}
23802      */
23803     isIdSelected : function(id){
23804         return (this.selections.key(id) ? true : false);
23805     },
23806
23807
23808     // private
23809     handleMouseDBClick : function(e, t){
23810         
23811     },
23812     // private
23813     handleMouseDown : function(e, t)
23814     {
23815             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23816         if(this.isLocked() || rowIndex < 0 ){
23817             return;
23818         };
23819         if(e.shiftKey && this.last !== false){
23820             var last = this.last;
23821             this.selectRange(last, rowIndex, e.ctrlKey);
23822             this.last = last; // reset the last
23823             t.focus();
23824     
23825         }else{
23826             var isSelected = this.isSelected(rowIndex);
23827             //Roo.log("select row:" + rowIndex);
23828             if(isSelected){
23829                 this.deselectRow(rowIndex);
23830             } else {
23831                         this.selectRow(rowIndex, true);
23832             }
23833     
23834             /*
23835                 if(e.button !== 0 && isSelected){
23836                 alert('rowIndex 2: ' + rowIndex);
23837                     view.focusRow(rowIndex);
23838                 }else if(e.ctrlKey && isSelected){
23839                     this.deselectRow(rowIndex);
23840                 }else if(!isSelected){
23841                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23842                     view.focusRow(rowIndex);
23843                 }
23844             */
23845         }
23846         this.fireEvent("afterselectionchange", this);
23847     },
23848     // private
23849     handleDragableRowClick :  function(grid, rowIndex, e) 
23850     {
23851         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23852             this.selectRow(rowIndex, false);
23853             grid.view.focusRow(rowIndex);
23854              this.fireEvent("afterselectionchange", this);
23855         }
23856     },
23857     
23858     /**
23859      * Selects multiple rows.
23860      * @param {Array} rows Array of the indexes of the row to select
23861      * @param {Boolean} keepExisting (optional) True to keep existing selections
23862      */
23863     selectRows : function(rows, keepExisting){
23864         if(!keepExisting){
23865             this.clearSelections();
23866         }
23867         for(var i = 0, len = rows.length; i < len; i++){
23868             this.selectRow(rows[i], true);
23869         }
23870     },
23871
23872     /**
23873      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23874      * @param {Number} startRow The index of the first row in the range
23875      * @param {Number} endRow The index of the last row in the range
23876      * @param {Boolean} keepExisting (optional) True to retain existing selections
23877      */
23878     selectRange : function(startRow, endRow, keepExisting){
23879         if(this.locked) {
23880             return;
23881         }
23882         if(!keepExisting){
23883             this.clearSelections();
23884         }
23885         if(startRow <= endRow){
23886             for(var i = startRow; i <= endRow; i++){
23887                 this.selectRow(i, true);
23888             }
23889         }else{
23890             for(var i = startRow; i >= endRow; i--){
23891                 this.selectRow(i, true);
23892             }
23893         }
23894     },
23895
23896     /**
23897      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23898      * @param {Number} startRow The index of the first row in the range
23899      * @param {Number} endRow The index of the last row in the range
23900      */
23901     deselectRange : function(startRow, endRow, preventViewNotify){
23902         if(this.locked) {
23903             return;
23904         }
23905         for(var i = startRow; i <= endRow; i++){
23906             this.deselectRow(i, preventViewNotify);
23907         }
23908     },
23909
23910     /**
23911      * Selects a row.
23912      * @param {Number} row The index of the row to select
23913      * @param {Boolean} keepExisting (optional) True to keep existing selections
23914      */
23915     selectRow : function(index, keepExisting, preventViewNotify)
23916     {
23917             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23918             return;
23919         }
23920         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23921             if(!keepExisting || this.singleSelect){
23922                 this.clearSelections();
23923             }
23924             
23925             var r = this.grid.store.getAt(index);
23926             //console.log('selectRow - record id :' + r.id);
23927             
23928             this.selections.add(r);
23929             this.last = this.lastActive = index;
23930             if(!preventViewNotify){
23931                 var proxy = new Roo.Element(
23932                                 this.grid.getRowDom(index)
23933                 );
23934                 proxy.addClass('bg-info info');
23935             }
23936             this.fireEvent("rowselect", this, index, r);
23937             this.fireEvent("selectionchange", this);
23938         }
23939     },
23940
23941     /**
23942      * Deselects a row.
23943      * @param {Number} row The index of the row to deselect
23944      */
23945     deselectRow : function(index, preventViewNotify)
23946     {
23947         if(this.locked) {
23948             return;
23949         }
23950         if(this.last == index){
23951             this.last = false;
23952         }
23953         if(this.lastActive == index){
23954             this.lastActive = false;
23955         }
23956         
23957         var r = this.grid.store.getAt(index);
23958         if (!r) {
23959             return;
23960         }
23961         
23962         this.selections.remove(r);
23963         //.console.log('deselectRow - record id :' + r.id);
23964         if(!preventViewNotify){
23965         
23966             var proxy = new Roo.Element(
23967                 this.grid.getRowDom(index)
23968             );
23969             proxy.removeClass('bg-info info');
23970         }
23971         this.fireEvent("rowdeselect", this, index);
23972         this.fireEvent("selectionchange", this);
23973     },
23974
23975     // private
23976     restoreLast : function(){
23977         if(this._last){
23978             this.last = this._last;
23979         }
23980     },
23981
23982     // private
23983     acceptsNav : function(row, col, cm){
23984         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23985     },
23986
23987     // private
23988     onEditorKey : function(field, e){
23989         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23990         if(k == e.TAB){
23991             e.stopEvent();
23992             ed.completeEdit();
23993             if(e.shiftKey){
23994                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23995             }else{
23996                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23997             }
23998         }else if(k == e.ENTER && !e.ctrlKey){
23999             e.stopEvent();
24000             ed.completeEdit();
24001             if(e.shiftKey){
24002                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24003             }else{
24004                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24005             }
24006         }else if(k == e.ESC){
24007             ed.cancelEdit();
24008         }
24009         if(newCell){
24010             g.startEditing(newCell[0], newCell[1]);
24011         }
24012     }
24013 });
24014 /*
24015  * Based on:
24016  * Ext JS Library 1.1.1
24017  * Copyright(c) 2006-2007, Ext JS, LLC.
24018  *
24019  * Originally Released Under LGPL - original licence link has changed is not relivant.
24020  *
24021  * Fork - LGPL
24022  * <script type="text/javascript">
24023  */
24024  
24025 /**
24026  * @class Roo.bootstrap.PagingToolbar
24027  * @extends Roo.bootstrap.NavSimplebar
24028  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24029  * @constructor
24030  * Create a new PagingToolbar
24031  * @param {Object} config The config object
24032  * @param {Roo.data.Store} store
24033  */
24034 Roo.bootstrap.PagingToolbar = function(config)
24035 {
24036     // old args format still supported... - xtype is prefered..
24037         // created from xtype...
24038     
24039     this.ds = config.dataSource;
24040     
24041     if (config.store && !this.ds) {
24042         this.store= Roo.factory(config.store, Roo.data);
24043         this.ds = this.store;
24044         this.ds.xmodule = this.xmodule || false;
24045     }
24046     
24047     this.toolbarItems = [];
24048     if (config.items) {
24049         this.toolbarItems = config.items;
24050     }
24051     
24052     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24053     
24054     this.cursor = 0;
24055     
24056     if (this.ds) { 
24057         this.bind(this.ds);
24058     }
24059     
24060     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24061     
24062 };
24063
24064 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24065     /**
24066      * @cfg {Roo.data.Store} dataSource
24067      * The underlying data store providing the paged data
24068      */
24069     /**
24070      * @cfg {String/HTMLElement/Element} container
24071      * container The id or element that will contain the toolbar
24072      */
24073     /**
24074      * @cfg {Boolean} displayInfo
24075      * True to display the displayMsg (defaults to false)
24076      */
24077     /**
24078      * @cfg {Number} pageSize
24079      * The number of records to display per page (defaults to 20)
24080      */
24081     pageSize: 20,
24082     /**
24083      * @cfg {String} displayMsg
24084      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24085      */
24086     displayMsg : 'Displaying {0} - {1} of {2}',
24087     /**
24088      * @cfg {String} emptyMsg
24089      * The message to display when no records are found (defaults to "No data to display")
24090      */
24091     emptyMsg : 'No data to display',
24092     /**
24093      * Customizable piece of the default paging text (defaults to "Page")
24094      * @type String
24095      */
24096     beforePageText : "Page",
24097     /**
24098      * Customizable piece of the default paging text (defaults to "of %0")
24099      * @type String
24100      */
24101     afterPageText : "of {0}",
24102     /**
24103      * Customizable piece of the default paging text (defaults to "First Page")
24104      * @type String
24105      */
24106     firstText : "First Page",
24107     /**
24108      * Customizable piece of the default paging text (defaults to "Previous Page")
24109      * @type String
24110      */
24111     prevText : "Previous Page",
24112     /**
24113      * Customizable piece of the default paging text (defaults to "Next Page")
24114      * @type String
24115      */
24116     nextText : "Next Page",
24117     /**
24118      * Customizable piece of the default paging text (defaults to "Last Page")
24119      * @type String
24120      */
24121     lastText : "Last Page",
24122     /**
24123      * Customizable piece of the default paging text (defaults to "Refresh")
24124      * @type String
24125      */
24126     refreshText : "Refresh",
24127
24128     buttons : false,
24129     // private
24130     onRender : function(ct, position) 
24131     {
24132         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24133         this.navgroup.parentId = this.id;
24134         this.navgroup.onRender(this.el, null);
24135         // add the buttons to the navgroup
24136         
24137         if(this.displayInfo){
24138             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24139             this.displayEl = this.el.select('.x-paging-info', true).first();
24140 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24141 //            this.displayEl = navel.el.select('span',true).first();
24142         }
24143         
24144         var _this = this;
24145         
24146         if(this.buttons){
24147             Roo.each(_this.buttons, function(e){ // this might need to use render????
24148                Roo.factory(e).onRender(_this.el, null);
24149             });
24150         }
24151             
24152         Roo.each(_this.toolbarItems, function(e) {
24153             _this.navgroup.addItem(e);
24154         });
24155         
24156         
24157         this.first = this.navgroup.addItem({
24158             tooltip: this.firstText,
24159             cls: "prev",
24160             icon : 'fa fa-backward',
24161             disabled: true,
24162             preventDefault: true,
24163             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24164         });
24165         
24166         this.prev =  this.navgroup.addItem({
24167             tooltip: this.prevText,
24168             cls: "prev",
24169             icon : 'fa fa-step-backward',
24170             disabled: true,
24171             preventDefault: true,
24172             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24173         });
24174     //this.addSeparator();
24175         
24176         
24177         var field = this.navgroup.addItem( {
24178             tagtype : 'span',
24179             cls : 'x-paging-position',
24180             
24181             html : this.beforePageText  +
24182                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24183                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24184          } ); //?? escaped?
24185         
24186         this.field = field.el.select('input', true).first();
24187         this.field.on("keydown", this.onPagingKeydown, this);
24188         this.field.on("focus", function(){this.dom.select();});
24189     
24190     
24191         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24192         //this.field.setHeight(18);
24193         //this.addSeparator();
24194         this.next = this.navgroup.addItem({
24195             tooltip: this.nextText,
24196             cls: "next",
24197             html : ' <i class="fa fa-step-forward">',
24198             disabled: true,
24199             preventDefault: true,
24200             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24201         });
24202         this.last = this.navgroup.addItem({
24203             tooltip: this.lastText,
24204             icon : 'fa fa-forward',
24205             cls: "next",
24206             disabled: true,
24207             preventDefault: true,
24208             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24209         });
24210     //this.addSeparator();
24211         this.loading = this.navgroup.addItem({
24212             tooltip: this.refreshText,
24213             icon: 'fa fa-refresh',
24214             preventDefault: true,
24215             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24216         });
24217         
24218     },
24219
24220     // private
24221     updateInfo : function(){
24222         if(this.displayEl){
24223             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24224             var msg = count == 0 ?
24225                 this.emptyMsg :
24226                 String.format(
24227                     this.displayMsg,
24228                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24229                 );
24230             this.displayEl.update(msg);
24231         }
24232     },
24233
24234     // private
24235     onLoad : function(ds, r, o)
24236     {
24237         this.cursor = o.params ? o.params.start : 0;
24238         var d = this.getPageData(),
24239             ap = d.activePage,
24240             ps = d.pages;
24241         
24242         
24243         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24244         this.field.dom.value = ap;
24245         this.first.setDisabled(ap == 1);
24246         this.prev.setDisabled(ap == 1);
24247         this.next.setDisabled(ap == ps);
24248         this.last.setDisabled(ap == ps);
24249         this.loading.enable();
24250         this.updateInfo();
24251     },
24252
24253     // private
24254     getPageData : function(){
24255         var total = this.ds.getTotalCount();
24256         return {
24257             total : total,
24258             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24259             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24260         };
24261     },
24262
24263     // private
24264     onLoadError : function(){
24265         this.loading.enable();
24266     },
24267
24268     // private
24269     onPagingKeydown : function(e){
24270         var k = e.getKey();
24271         var d = this.getPageData();
24272         if(k == e.RETURN){
24273             var v = this.field.dom.value, pageNum;
24274             if(!v || isNaN(pageNum = parseInt(v, 10))){
24275                 this.field.dom.value = d.activePage;
24276                 return;
24277             }
24278             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24279             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24280             e.stopEvent();
24281         }
24282         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))
24283         {
24284           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24285           this.field.dom.value = pageNum;
24286           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24287           e.stopEvent();
24288         }
24289         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24290         {
24291           var v = this.field.dom.value, pageNum; 
24292           var increment = (e.shiftKey) ? 10 : 1;
24293           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24294                 increment *= -1;
24295           }
24296           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24297             this.field.dom.value = d.activePage;
24298             return;
24299           }
24300           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24301           {
24302             this.field.dom.value = parseInt(v, 10) + increment;
24303             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24304             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24305           }
24306           e.stopEvent();
24307         }
24308     },
24309
24310     // private
24311     beforeLoad : function(){
24312         if(this.loading){
24313             this.loading.disable();
24314         }
24315     },
24316
24317     // private
24318     onClick : function(which){
24319         
24320         var ds = this.ds;
24321         if (!ds) {
24322             return;
24323         }
24324         
24325         switch(which){
24326             case "first":
24327                 ds.load({params:{start: 0, limit: this.pageSize}});
24328             break;
24329             case "prev":
24330                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24331             break;
24332             case "next":
24333                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24334             break;
24335             case "last":
24336                 var total = ds.getTotalCount();
24337                 var extra = total % this.pageSize;
24338                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24339                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24340             break;
24341             case "refresh":
24342                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24343             break;
24344         }
24345     },
24346
24347     /**
24348      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24349      * @param {Roo.data.Store} store The data store to unbind
24350      */
24351     unbind : function(ds){
24352         ds.un("beforeload", this.beforeLoad, this);
24353         ds.un("load", this.onLoad, this);
24354         ds.un("loadexception", this.onLoadError, this);
24355         ds.un("remove", this.updateInfo, this);
24356         ds.un("add", this.updateInfo, this);
24357         this.ds = undefined;
24358     },
24359
24360     /**
24361      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24362      * @param {Roo.data.Store} store The data store to bind
24363      */
24364     bind : function(ds){
24365         ds.on("beforeload", this.beforeLoad, this);
24366         ds.on("load", this.onLoad, this);
24367         ds.on("loadexception", this.onLoadError, this);
24368         ds.on("remove", this.updateInfo, this);
24369         ds.on("add", this.updateInfo, this);
24370         this.ds = ds;
24371     }
24372 });/*
24373  * - LGPL
24374  *
24375  * element
24376  * 
24377  */
24378
24379 /**
24380  * @class Roo.bootstrap.MessageBar
24381  * @extends Roo.bootstrap.Component
24382  * Bootstrap MessageBar class
24383  * @cfg {String} html contents of the MessageBar
24384  * @cfg {String} weight (info | success | warning | danger) default info
24385  * @cfg {String} beforeClass insert the bar before the given class
24386  * @cfg {Boolean} closable (true | false) default false
24387  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24388  * 
24389  * @constructor
24390  * Create a new Element
24391  * @param {Object} config The config object
24392  */
24393
24394 Roo.bootstrap.MessageBar = function(config){
24395     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24396 };
24397
24398 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24399     
24400     html: '',
24401     weight: 'info',
24402     closable: false,
24403     fixed: false,
24404     beforeClass: 'bootstrap-sticky-wrap',
24405     
24406     getAutoCreate : function(){
24407         
24408         var cfg = {
24409             tag: 'div',
24410             cls: 'alert alert-dismissable alert-' + this.weight,
24411             cn: [
24412                 {
24413                     tag: 'span',
24414                     cls: 'message',
24415                     html: this.html || ''
24416                 }
24417             ]
24418         };
24419         
24420         if(this.fixed){
24421             cfg.cls += ' alert-messages-fixed';
24422         }
24423         
24424         if(this.closable){
24425             cfg.cn.push({
24426                 tag: 'button',
24427                 cls: 'close',
24428                 html: 'x'
24429             });
24430         }
24431         
24432         return cfg;
24433     },
24434     
24435     onRender : function(ct, position)
24436     {
24437         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24438         
24439         if(!this.el){
24440             var cfg = Roo.apply({},  this.getAutoCreate());
24441             cfg.id = Roo.id();
24442             
24443             if (this.cls) {
24444                 cfg.cls += ' ' + this.cls;
24445             }
24446             if (this.style) {
24447                 cfg.style = this.style;
24448             }
24449             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24450             
24451             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24452         }
24453         
24454         this.el.select('>button.close').on('click', this.hide, this);
24455         
24456     },
24457     
24458     show : function()
24459     {
24460         if (!this.rendered) {
24461             this.render();
24462         }
24463         
24464         this.el.show();
24465         
24466         this.fireEvent('show', this);
24467         
24468     },
24469     
24470     hide : function()
24471     {
24472         if (!this.rendered) {
24473             this.render();
24474         }
24475         
24476         this.el.hide();
24477         
24478         this.fireEvent('hide', this);
24479     },
24480     
24481     update : function()
24482     {
24483 //        var e = this.el.dom.firstChild;
24484 //        
24485 //        if(this.closable){
24486 //            e = e.nextSibling;
24487 //        }
24488 //        
24489 //        e.data = this.html || '';
24490
24491         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24492     }
24493    
24494 });
24495
24496  
24497
24498      /*
24499  * - LGPL
24500  *
24501  * Graph
24502  * 
24503  */
24504
24505
24506 /**
24507  * @class Roo.bootstrap.Graph
24508  * @extends Roo.bootstrap.Component
24509  * Bootstrap Graph class
24510 > Prameters
24511  -sm {number} sm 4
24512  -md {number} md 5
24513  @cfg {String} graphtype  bar | vbar | pie
24514  @cfg {number} g_x coodinator | centre x (pie)
24515  @cfg {number} g_y coodinator | centre y (pie)
24516  @cfg {number} g_r radius (pie)
24517  @cfg {number} g_height height of the chart (respected by all elements in the set)
24518  @cfg {number} g_width width of the chart (respected by all elements in the set)
24519  @cfg {Object} title The title of the chart
24520     
24521  -{Array}  values
24522  -opts (object) options for the chart 
24523      o {
24524      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24525      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24526      o vgutter (number)
24527      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.
24528      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24529      o to
24530      o stretch (boolean)
24531      o }
24532  -opts (object) options for the pie
24533      o{
24534      o cut
24535      o startAngle (number)
24536      o endAngle (number)
24537      } 
24538  *
24539  * @constructor
24540  * Create a new Input
24541  * @param {Object} config The config object
24542  */
24543
24544 Roo.bootstrap.Graph = function(config){
24545     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24546     
24547     this.addEvents({
24548         // img events
24549         /**
24550          * @event click
24551          * The img click event for the img.
24552          * @param {Roo.EventObject} e
24553          */
24554         "click" : true
24555     });
24556 };
24557
24558 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24559     
24560     sm: 4,
24561     md: 5,
24562     graphtype: 'bar',
24563     g_height: 250,
24564     g_width: 400,
24565     g_x: 50,
24566     g_y: 50,
24567     g_r: 30,
24568     opts:{
24569         //g_colors: this.colors,
24570         g_type: 'soft',
24571         g_gutter: '20%'
24572
24573     },
24574     title : false,
24575
24576     getAutoCreate : function(){
24577         
24578         var cfg = {
24579             tag: 'div',
24580             html : null
24581         };
24582         
24583         
24584         return  cfg;
24585     },
24586
24587     onRender : function(ct,position){
24588         
24589         
24590         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24591         
24592         if (typeof(Raphael) == 'undefined') {
24593             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24594             return;
24595         }
24596         
24597         this.raphael = Raphael(this.el.dom);
24598         
24599                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24600                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24601                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24602                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24603                 /*
24604                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24605                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24606                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24607                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24608                 
24609                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24610                 r.barchart(330, 10, 300, 220, data1);
24611                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24612                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24613                 */
24614                 
24615                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24616                 // r.barchart(30, 30, 560, 250,  xdata, {
24617                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24618                 //     axis : "0 0 1 1",
24619                 //     axisxlabels :  xdata
24620                 //     //yvalues : cols,
24621                    
24622                 // });
24623 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24624 //        
24625 //        this.load(null,xdata,{
24626 //                axis : "0 0 1 1",
24627 //                axisxlabels :  xdata
24628 //                });
24629
24630     },
24631
24632     load : function(graphtype,xdata,opts)
24633     {
24634         this.raphael.clear();
24635         if(!graphtype) {
24636             graphtype = this.graphtype;
24637         }
24638         if(!opts){
24639             opts = this.opts;
24640         }
24641         var r = this.raphael,
24642             fin = function () {
24643                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24644             },
24645             fout = function () {
24646                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24647             },
24648             pfin = function() {
24649                 this.sector.stop();
24650                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24651
24652                 if (this.label) {
24653                     this.label[0].stop();
24654                     this.label[0].attr({ r: 7.5 });
24655                     this.label[1].attr({ "font-weight": 800 });
24656                 }
24657             },
24658             pfout = function() {
24659                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24660
24661                 if (this.label) {
24662                     this.label[0].animate({ r: 5 }, 500, "bounce");
24663                     this.label[1].attr({ "font-weight": 400 });
24664                 }
24665             };
24666
24667         switch(graphtype){
24668             case 'bar':
24669                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24670                 break;
24671             case 'hbar':
24672                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24673                 break;
24674             case 'pie':
24675 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24676 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24677 //            
24678                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24679                 
24680                 break;
24681
24682         }
24683         
24684         if(this.title){
24685             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24686         }
24687         
24688     },
24689     
24690     setTitle: function(o)
24691     {
24692         this.title = o;
24693     },
24694     
24695     initEvents: function() {
24696         
24697         if(!this.href){
24698             this.el.on('click', this.onClick, this);
24699         }
24700     },
24701     
24702     onClick : function(e)
24703     {
24704         Roo.log('img onclick');
24705         this.fireEvent('click', this, e);
24706     }
24707    
24708 });
24709
24710  
24711 /*
24712  * - LGPL
24713  *
24714  * numberBox
24715  * 
24716  */
24717 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24718
24719 /**
24720  * @class Roo.bootstrap.dash.NumberBox
24721  * @extends Roo.bootstrap.Component
24722  * Bootstrap NumberBox class
24723  * @cfg {String} headline Box headline
24724  * @cfg {String} content Box content
24725  * @cfg {String} icon Box icon
24726  * @cfg {String} footer Footer text
24727  * @cfg {String} fhref Footer href
24728  * 
24729  * @constructor
24730  * Create a new NumberBox
24731  * @param {Object} config The config object
24732  */
24733
24734
24735 Roo.bootstrap.dash.NumberBox = function(config){
24736     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24737     
24738 };
24739
24740 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24741     
24742     headline : '',
24743     content : '',
24744     icon : '',
24745     footer : '',
24746     fhref : '',
24747     ficon : '',
24748     
24749     getAutoCreate : function(){
24750         
24751         var cfg = {
24752             tag : 'div',
24753             cls : 'small-box ',
24754             cn : [
24755                 {
24756                     tag : 'div',
24757                     cls : 'inner',
24758                     cn :[
24759                         {
24760                             tag : 'h3',
24761                             cls : 'roo-headline',
24762                             html : this.headline
24763                         },
24764                         {
24765                             tag : 'p',
24766                             cls : 'roo-content',
24767                             html : this.content
24768                         }
24769                     ]
24770                 }
24771             ]
24772         };
24773         
24774         if(this.icon){
24775             cfg.cn.push({
24776                 tag : 'div',
24777                 cls : 'icon',
24778                 cn :[
24779                     {
24780                         tag : 'i',
24781                         cls : 'ion ' + this.icon
24782                     }
24783                 ]
24784             });
24785         }
24786         
24787         if(this.footer){
24788             var footer = {
24789                 tag : 'a',
24790                 cls : 'small-box-footer',
24791                 href : this.fhref || '#',
24792                 html : this.footer
24793             };
24794             
24795             cfg.cn.push(footer);
24796             
24797         }
24798         
24799         return  cfg;
24800     },
24801
24802     onRender : function(ct,position){
24803         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24804
24805
24806        
24807                 
24808     },
24809
24810     setHeadline: function (value)
24811     {
24812         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24813     },
24814     
24815     setFooter: function (value, href)
24816     {
24817         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24818         
24819         if(href){
24820             this.el.select('a.small-box-footer',true).first().attr('href', href);
24821         }
24822         
24823     },
24824
24825     setContent: function (value)
24826     {
24827         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24828     },
24829
24830     initEvents: function() 
24831     {   
24832         
24833     }
24834     
24835 });
24836
24837  
24838 /*
24839  * - LGPL
24840  *
24841  * TabBox
24842  * 
24843  */
24844 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24845
24846 /**
24847  * @class Roo.bootstrap.dash.TabBox
24848  * @extends Roo.bootstrap.Component
24849  * Bootstrap TabBox class
24850  * @cfg {String} title Title of the TabBox
24851  * @cfg {String} icon Icon of the TabBox
24852  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24853  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24854  * 
24855  * @constructor
24856  * Create a new TabBox
24857  * @param {Object} config The config object
24858  */
24859
24860
24861 Roo.bootstrap.dash.TabBox = function(config){
24862     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24863     this.addEvents({
24864         // raw events
24865         /**
24866          * @event addpane
24867          * When a pane is added
24868          * @param {Roo.bootstrap.dash.TabPane} pane
24869          */
24870         "addpane" : true,
24871         /**
24872          * @event activatepane
24873          * When a pane is activated
24874          * @param {Roo.bootstrap.dash.TabPane} pane
24875          */
24876         "activatepane" : true
24877         
24878          
24879     });
24880     
24881     this.panes = [];
24882 };
24883
24884 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24885
24886     title : '',
24887     icon : false,
24888     showtabs : true,
24889     tabScrollable : false,
24890     
24891     getChildContainer : function()
24892     {
24893         return this.el.select('.tab-content', true).first();
24894     },
24895     
24896     getAutoCreate : function(){
24897         
24898         var header = {
24899             tag: 'li',
24900             cls: 'pull-left header',
24901             html: this.title,
24902             cn : []
24903         };
24904         
24905         if(this.icon){
24906             header.cn.push({
24907                 tag: 'i',
24908                 cls: 'fa ' + this.icon
24909             });
24910         }
24911         
24912         var h = {
24913             tag: 'ul',
24914             cls: 'nav nav-tabs pull-right',
24915             cn: [
24916                 header
24917             ]
24918         };
24919         
24920         if(this.tabScrollable){
24921             h = {
24922                 tag: 'div',
24923                 cls: 'tab-header',
24924                 cn: [
24925                     {
24926                         tag: 'ul',
24927                         cls: 'nav nav-tabs pull-right',
24928                         cn: [
24929                             header
24930                         ]
24931                     }
24932                 ]
24933             };
24934         }
24935         
24936         var cfg = {
24937             tag: 'div',
24938             cls: 'nav-tabs-custom',
24939             cn: [
24940                 h,
24941                 {
24942                     tag: 'div',
24943                     cls: 'tab-content no-padding',
24944                     cn: []
24945                 }
24946             ]
24947         };
24948
24949         return  cfg;
24950     },
24951     initEvents : function()
24952     {
24953         //Roo.log('add add pane handler');
24954         this.on('addpane', this.onAddPane, this);
24955     },
24956      /**
24957      * Updates the box title
24958      * @param {String} html to set the title to.
24959      */
24960     setTitle : function(value)
24961     {
24962         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24963     },
24964     onAddPane : function(pane)
24965     {
24966         this.panes.push(pane);
24967         //Roo.log('addpane');
24968         //Roo.log(pane);
24969         // tabs are rendere left to right..
24970         if(!this.showtabs){
24971             return;
24972         }
24973         
24974         var ctr = this.el.select('.nav-tabs', true).first();
24975          
24976          
24977         var existing = ctr.select('.nav-tab',true);
24978         var qty = existing.getCount();;
24979         
24980         
24981         var tab = ctr.createChild({
24982             tag : 'li',
24983             cls : 'nav-tab' + (qty ? '' : ' active'),
24984             cn : [
24985                 {
24986                     tag : 'a',
24987                     href:'#',
24988                     html : pane.title
24989                 }
24990             ]
24991         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24992         pane.tab = tab;
24993         
24994         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24995         if (!qty) {
24996             pane.el.addClass('active');
24997         }
24998         
24999                 
25000     },
25001     onTabClick : function(ev,un,ob,pane)
25002     {
25003         //Roo.log('tab - prev default');
25004         ev.preventDefault();
25005         
25006         
25007         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25008         pane.tab.addClass('active');
25009         //Roo.log(pane.title);
25010         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25011         // technically we should have a deactivate event.. but maybe add later.
25012         // and it should not de-activate the selected tab...
25013         this.fireEvent('activatepane', pane);
25014         pane.el.addClass('active');
25015         pane.fireEvent('activate');
25016         
25017         
25018     },
25019     
25020     getActivePane : function()
25021     {
25022         var r = false;
25023         Roo.each(this.panes, function(p) {
25024             if(p.el.hasClass('active')){
25025                 r = p;
25026                 return false;
25027             }
25028             
25029             return;
25030         });
25031         
25032         return r;
25033     }
25034     
25035     
25036 });
25037
25038  
25039 /*
25040  * - LGPL
25041  *
25042  * Tab pane
25043  * 
25044  */
25045 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25046 /**
25047  * @class Roo.bootstrap.TabPane
25048  * @extends Roo.bootstrap.Component
25049  * Bootstrap TabPane class
25050  * @cfg {Boolean} active (false | true) Default false
25051  * @cfg {String} title title of panel
25052
25053  * 
25054  * @constructor
25055  * Create a new TabPane
25056  * @param {Object} config The config object
25057  */
25058
25059 Roo.bootstrap.dash.TabPane = function(config){
25060     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25061     
25062     this.addEvents({
25063         // raw events
25064         /**
25065          * @event activate
25066          * When a pane is activated
25067          * @param {Roo.bootstrap.dash.TabPane} pane
25068          */
25069         "activate" : true
25070          
25071     });
25072 };
25073
25074 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25075     
25076     active : false,
25077     title : '',
25078     
25079     // the tabBox that this is attached to.
25080     tab : false,
25081      
25082     getAutoCreate : function() 
25083     {
25084         var cfg = {
25085             tag: 'div',
25086             cls: 'tab-pane'
25087         };
25088         
25089         if(this.active){
25090             cfg.cls += ' active';
25091         }
25092         
25093         return cfg;
25094     },
25095     initEvents  : function()
25096     {
25097         //Roo.log('trigger add pane handler');
25098         this.parent().fireEvent('addpane', this)
25099     },
25100     
25101      /**
25102      * Updates the tab title 
25103      * @param {String} html to set the title to.
25104      */
25105     setTitle: function(str)
25106     {
25107         if (!this.tab) {
25108             return;
25109         }
25110         this.title = str;
25111         this.tab.select('a', true).first().dom.innerHTML = str;
25112         
25113     }
25114     
25115     
25116     
25117 });
25118
25119  
25120
25121
25122  /*
25123  * - LGPL
25124  *
25125  * menu
25126  * 
25127  */
25128 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25129
25130 /**
25131  * @class Roo.bootstrap.menu.Menu
25132  * @extends Roo.bootstrap.Component
25133  * Bootstrap Menu class - container for Menu
25134  * @cfg {String} html Text of the menu
25135  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25136  * @cfg {String} icon Font awesome icon
25137  * @cfg {String} pos Menu align to (top | bottom) default bottom
25138  * 
25139  * 
25140  * @constructor
25141  * Create a new Menu
25142  * @param {Object} config The config object
25143  */
25144
25145
25146 Roo.bootstrap.menu.Menu = function(config){
25147     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25148     
25149     this.addEvents({
25150         /**
25151          * @event beforeshow
25152          * Fires before this menu is displayed
25153          * @param {Roo.bootstrap.menu.Menu} this
25154          */
25155         beforeshow : true,
25156         /**
25157          * @event beforehide
25158          * Fires before this menu is hidden
25159          * @param {Roo.bootstrap.menu.Menu} this
25160          */
25161         beforehide : true,
25162         /**
25163          * @event show
25164          * Fires after this menu is displayed
25165          * @param {Roo.bootstrap.menu.Menu} this
25166          */
25167         show : true,
25168         /**
25169          * @event hide
25170          * Fires after this menu is hidden
25171          * @param {Roo.bootstrap.menu.Menu} this
25172          */
25173         hide : true,
25174         /**
25175          * @event click
25176          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25177          * @param {Roo.bootstrap.menu.Menu} this
25178          * @param {Roo.EventObject} e
25179          */
25180         click : true
25181     });
25182     
25183 };
25184
25185 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25186     
25187     submenu : false,
25188     html : '',
25189     weight : 'default',
25190     icon : false,
25191     pos : 'bottom',
25192     
25193     
25194     getChildContainer : function() {
25195         if(this.isSubMenu){
25196             return this.el;
25197         }
25198         
25199         return this.el.select('ul.dropdown-menu', true).first();  
25200     },
25201     
25202     getAutoCreate : function()
25203     {
25204         var text = [
25205             {
25206                 tag : 'span',
25207                 cls : 'roo-menu-text',
25208                 html : this.html
25209             }
25210         ];
25211         
25212         if(this.icon){
25213             text.unshift({
25214                 tag : 'i',
25215                 cls : 'fa ' + this.icon
25216             })
25217         }
25218         
25219         
25220         var cfg = {
25221             tag : 'div',
25222             cls : 'btn-group',
25223             cn : [
25224                 {
25225                     tag : 'button',
25226                     cls : 'dropdown-button btn btn-' + this.weight,
25227                     cn : text
25228                 },
25229                 {
25230                     tag : 'button',
25231                     cls : 'dropdown-toggle btn btn-' + this.weight,
25232                     cn : [
25233                         {
25234                             tag : 'span',
25235                             cls : 'caret'
25236                         }
25237                     ]
25238                 },
25239                 {
25240                     tag : 'ul',
25241                     cls : 'dropdown-menu'
25242                 }
25243             ]
25244             
25245         };
25246         
25247         if(this.pos == 'top'){
25248             cfg.cls += ' dropup';
25249         }
25250         
25251         if(this.isSubMenu){
25252             cfg = {
25253                 tag : 'ul',
25254                 cls : 'dropdown-menu'
25255             }
25256         }
25257         
25258         return cfg;
25259     },
25260     
25261     onRender : function(ct, position)
25262     {
25263         this.isSubMenu = ct.hasClass('dropdown-submenu');
25264         
25265         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25266     },
25267     
25268     initEvents : function() 
25269     {
25270         if(this.isSubMenu){
25271             return;
25272         }
25273         
25274         this.hidden = true;
25275         
25276         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25277         this.triggerEl.on('click', this.onTriggerPress, this);
25278         
25279         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25280         this.buttonEl.on('click', this.onClick, this);
25281         
25282     },
25283     
25284     list : function()
25285     {
25286         if(this.isSubMenu){
25287             return this.el;
25288         }
25289         
25290         return this.el.select('ul.dropdown-menu', true).first();
25291     },
25292     
25293     onClick : function(e)
25294     {
25295         this.fireEvent("click", this, e);
25296     },
25297     
25298     onTriggerPress  : function(e)
25299     {   
25300         if (this.isVisible()) {
25301             this.hide();
25302         } else {
25303             this.show();
25304         }
25305     },
25306     
25307     isVisible : function(){
25308         return !this.hidden;
25309     },
25310     
25311     show : function()
25312     {
25313         this.fireEvent("beforeshow", this);
25314         
25315         this.hidden = false;
25316         this.el.addClass('open');
25317         
25318         Roo.get(document).on("mouseup", this.onMouseUp, this);
25319         
25320         this.fireEvent("show", this);
25321         
25322         
25323     },
25324     
25325     hide : function()
25326     {
25327         this.fireEvent("beforehide", this);
25328         
25329         this.hidden = true;
25330         this.el.removeClass('open');
25331         
25332         Roo.get(document).un("mouseup", this.onMouseUp);
25333         
25334         this.fireEvent("hide", this);
25335     },
25336     
25337     onMouseUp : function()
25338     {
25339         this.hide();
25340     }
25341     
25342 });
25343
25344  
25345  /*
25346  * - LGPL
25347  *
25348  * menu item
25349  * 
25350  */
25351 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25352
25353 /**
25354  * @class Roo.bootstrap.menu.Item
25355  * @extends Roo.bootstrap.Component
25356  * Bootstrap MenuItem class
25357  * @cfg {Boolean} submenu (true | false) default false
25358  * @cfg {String} html text of the item
25359  * @cfg {String} href the link
25360  * @cfg {Boolean} disable (true | false) default false
25361  * @cfg {Boolean} preventDefault (true | false) default true
25362  * @cfg {String} icon Font awesome icon
25363  * @cfg {String} pos Submenu align to (left | right) default right 
25364  * 
25365  * 
25366  * @constructor
25367  * Create a new Item
25368  * @param {Object} config The config object
25369  */
25370
25371
25372 Roo.bootstrap.menu.Item = function(config){
25373     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25374     this.addEvents({
25375         /**
25376          * @event mouseover
25377          * Fires when the mouse is hovering over this menu
25378          * @param {Roo.bootstrap.menu.Item} this
25379          * @param {Roo.EventObject} e
25380          */
25381         mouseover : true,
25382         /**
25383          * @event mouseout
25384          * Fires when the mouse exits this menu
25385          * @param {Roo.bootstrap.menu.Item} this
25386          * @param {Roo.EventObject} e
25387          */
25388         mouseout : true,
25389         // raw events
25390         /**
25391          * @event click
25392          * The raw click event for the entire grid.
25393          * @param {Roo.EventObject} e
25394          */
25395         click : true
25396     });
25397 };
25398
25399 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25400     
25401     submenu : false,
25402     href : '',
25403     html : '',
25404     preventDefault: true,
25405     disable : false,
25406     icon : false,
25407     pos : 'right',
25408     
25409     getAutoCreate : function()
25410     {
25411         var text = [
25412             {
25413                 tag : 'span',
25414                 cls : 'roo-menu-item-text',
25415                 html : this.html
25416             }
25417         ];
25418         
25419         if(this.icon){
25420             text.unshift({
25421                 tag : 'i',
25422                 cls : 'fa ' + this.icon
25423             })
25424         }
25425         
25426         var cfg = {
25427             tag : 'li',
25428             cn : [
25429                 {
25430                     tag : 'a',
25431                     href : this.href || '#',
25432                     cn : text
25433                 }
25434             ]
25435         };
25436         
25437         if(this.disable){
25438             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25439         }
25440         
25441         if(this.submenu){
25442             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25443             
25444             if(this.pos == 'left'){
25445                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25446             }
25447         }
25448         
25449         return cfg;
25450     },
25451     
25452     initEvents : function() 
25453     {
25454         this.el.on('mouseover', this.onMouseOver, this);
25455         this.el.on('mouseout', this.onMouseOut, this);
25456         
25457         this.el.select('a', true).first().on('click', this.onClick, this);
25458         
25459     },
25460     
25461     onClick : function(e)
25462     {
25463         if(this.preventDefault){
25464             e.preventDefault();
25465         }
25466         
25467         this.fireEvent("click", this, e);
25468     },
25469     
25470     onMouseOver : function(e)
25471     {
25472         if(this.submenu && this.pos == 'left'){
25473             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25474         }
25475         
25476         this.fireEvent("mouseover", this, e);
25477     },
25478     
25479     onMouseOut : function(e)
25480     {
25481         this.fireEvent("mouseout", this, e);
25482     }
25483 });
25484
25485  
25486
25487  /*
25488  * - LGPL
25489  *
25490  * menu separator
25491  * 
25492  */
25493 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25494
25495 /**
25496  * @class Roo.bootstrap.menu.Separator
25497  * @extends Roo.bootstrap.Component
25498  * Bootstrap Separator class
25499  * 
25500  * @constructor
25501  * Create a new Separator
25502  * @param {Object} config The config object
25503  */
25504
25505
25506 Roo.bootstrap.menu.Separator = function(config){
25507     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25508 };
25509
25510 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25511     
25512     getAutoCreate : function(){
25513         var cfg = {
25514             tag : 'li',
25515             cls: 'divider'
25516         };
25517         
25518         return cfg;
25519     }
25520    
25521 });
25522
25523  
25524
25525  /*
25526  * - LGPL
25527  *
25528  * Tooltip
25529  * 
25530  */
25531
25532 /**
25533  * @class Roo.bootstrap.Tooltip
25534  * Bootstrap Tooltip class
25535  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25536  * to determine which dom element triggers the tooltip.
25537  * 
25538  * It needs to add support for additional attributes like tooltip-position
25539  * 
25540  * @constructor
25541  * Create a new Toolti
25542  * @param {Object} config The config object
25543  */
25544
25545 Roo.bootstrap.Tooltip = function(config){
25546     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25547     
25548     this.alignment = Roo.bootstrap.Tooltip.alignment;
25549     
25550     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25551         this.alignment = config.alignment;
25552     }
25553     
25554 };
25555
25556 Roo.apply(Roo.bootstrap.Tooltip, {
25557     /**
25558      * @function init initialize tooltip monitoring.
25559      * @static
25560      */
25561     currentEl : false,
25562     currentTip : false,
25563     currentRegion : false,
25564     
25565     //  init : delay?
25566     
25567     init : function()
25568     {
25569         Roo.get(document).on('mouseover', this.enter ,this);
25570         Roo.get(document).on('mouseout', this.leave, this);
25571          
25572         
25573         this.currentTip = new Roo.bootstrap.Tooltip();
25574     },
25575     
25576     enter : function(ev)
25577     {
25578         var dom = ev.getTarget();
25579         
25580         //Roo.log(['enter',dom]);
25581         var el = Roo.fly(dom);
25582         if (this.currentEl) {
25583             //Roo.log(dom);
25584             //Roo.log(this.currentEl);
25585             //Roo.log(this.currentEl.contains(dom));
25586             if (this.currentEl == el) {
25587                 return;
25588             }
25589             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25590                 return;
25591             }
25592
25593         }
25594         
25595         if (this.currentTip.el) {
25596             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25597         }    
25598         //Roo.log(ev);
25599         
25600         if(!el || el.dom == document){
25601             return;
25602         }
25603         
25604         var bindEl = el;
25605         
25606         // you can not look for children, as if el is the body.. then everythign is the child..
25607         if (!el.attr('tooltip')) { //
25608             if (!el.select("[tooltip]").elements.length) {
25609                 return;
25610             }
25611             // is the mouse over this child...?
25612             bindEl = el.select("[tooltip]").first();
25613             var xy = ev.getXY();
25614             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25615                 //Roo.log("not in region.");
25616                 return;
25617             }
25618             //Roo.log("child element over..");
25619             
25620         }
25621         this.currentEl = bindEl;
25622         this.currentTip.bind(bindEl);
25623         this.currentRegion = Roo.lib.Region.getRegion(dom);
25624         this.currentTip.enter();
25625         
25626     },
25627     leave : function(ev)
25628     {
25629         var dom = ev.getTarget();
25630         //Roo.log(['leave',dom]);
25631         if (!this.currentEl) {
25632             return;
25633         }
25634         
25635         
25636         if (dom != this.currentEl.dom) {
25637             return;
25638         }
25639         var xy = ev.getXY();
25640         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25641             return;
25642         }
25643         // only activate leave if mouse cursor is outside... bounding box..
25644         
25645         
25646         
25647         
25648         if (this.currentTip) {
25649             this.currentTip.leave();
25650         }
25651         //Roo.log('clear currentEl');
25652         this.currentEl = false;
25653         
25654         
25655     },
25656     alignment : {
25657         'left' : ['r-l', [-2,0], 'right'],
25658         'right' : ['l-r', [2,0], 'left'],
25659         'bottom' : ['t-b', [0,2], 'top'],
25660         'top' : [ 'b-t', [0,-2], 'bottom']
25661     }
25662     
25663 });
25664
25665
25666 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25667     
25668     
25669     bindEl : false,
25670     
25671     delay : null, // can be { show : 300 , hide: 500}
25672     
25673     timeout : null,
25674     
25675     hoverState : null, //???
25676     
25677     placement : 'bottom', 
25678     
25679     alignment : false,
25680     
25681     getAutoCreate : function(){
25682     
25683         var cfg = {
25684            cls : 'tooltip',
25685            role : 'tooltip',
25686            cn : [
25687                 {
25688                     cls : 'tooltip-arrow'
25689                 },
25690                 {
25691                     cls : 'tooltip-inner'
25692                 }
25693            ]
25694         };
25695         
25696         return cfg;
25697     },
25698     bind : function(el)
25699     {
25700         this.bindEl = el;
25701     },
25702       
25703     
25704     enter : function () {
25705        
25706         if (this.timeout != null) {
25707             clearTimeout(this.timeout);
25708         }
25709         
25710         this.hoverState = 'in';
25711          //Roo.log("enter - show");
25712         if (!this.delay || !this.delay.show) {
25713             this.show();
25714             return;
25715         }
25716         var _t = this;
25717         this.timeout = setTimeout(function () {
25718             if (_t.hoverState == 'in') {
25719                 _t.show();
25720             }
25721         }, this.delay.show);
25722     },
25723     leave : function()
25724     {
25725         clearTimeout(this.timeout);
25726     
25727         this.hoverState = 'out';
25728          if (!this.delay || !this.delay.hide) {
25729             this.hide();
25730             return;
25731         }
25732        
25733         var _t = this;
25734         this.timeout = setTimeout(function () {
25735             //Roo.log("leave - timeout");
25736             
25737             if (_t.hoverState == 'out') {
25738                 _t.hide();
25739                 Roo.bootstrap.Tooltip.currentEl = false;
25740             }
25741         }, delay);
25742     },
25743     
25744     show : function (msg)
25745     {
25746         if (!this.el) {
25747             this.render(document.body);
25748         }
25749         // set content.
25750         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25751         
25752         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25753         
25754         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25755         
25756         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25757         
25758         var placement = typeof this.placement == 'function' ?
25759             this.placement.call(this, this.el, on_el) :
25760             this.placement;
25761             
25762         var autoToken = /\s?auto?\s?/i;
25763         var autoPlace = autoToken.test(placement);
25764         if (autoPlace) {
25765             placement = placement.replace(autoToken, '') || 'top';
25766         }
25767         
25768         //this.el.detach()
25769         //this.el.setXY([0,0]);
25770         this.el.show();
25771         //this.el.dom.style.display='block';
25772         
25773         //this.el.appendTo(on_el);
25774         
25775         var p = this.getPosition();
25776         var box = this.el.getBox();
25777         
25778         if (autoPlace) {
25779             // fixme..
25780         }
25781         
25782         var align = this.alignment[placement];
25783         
25784         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25785         
25786         if(placement == 'top' || placement == 'bottom'){
25787             if(xy[0] < 0){
25788                 placement = 'right';
25789             }
25790             
25791             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25792                 placement = 'left';
25793             }
25794             
25795             var scroll = Roo.select('body', true).first().getScroll();
25796             
25797             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25798                 placement = 'top';
25799             }
25800             
25801         }
25802         
25803         this.el.alignTo(this.bindEl, align[0],align[1]);
25804         //var arrow = this.el.select('.arrow',true).first();
25805         //arrow.set(align[2], 
25806         
25807         this.el.addClass(placement);
25808         
25809         this.el.addClass('in fade');
25810         
25811         this.hoverState = null;
25812         
25813         if (this.el.hasClass('fade')) {
25814             // fade it?
25815         }
25816         
25817     },
25818     hide : function()
25819     {
25820          
25821         if (!this.el) {
25822             return;
25823         }
25824         //this.el.setXY([0,0]);
25825         this.el.removeClass('in');
25826         //this.el.hide();
25827         
25828     }
25829     
25830 });
25831  
25832
25833  /*
25834  * - LGPL
25835  *
25836  * Location Picker
25837  * 
25838  */
25839
25840 /**
25841  * @class Roo.bootstrap.LocationPicker
25842  * @extends Roo.bootstrap.Component
25843  * Bootstrap LocationPicker class
25844  * @cfg {Number} latitude Position when init default 0
25845  * @cfg {Number} longitude Position when init default 0
25846  * @cfg {Number} zoom default 15
25847  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25848  * @cfg {Boolean} mapTypeControl default false
25849  * @cfg {Boolean} disableDoubleClickZoom default false
25850  * @cfg {Boolean} scrollwheel default true
25851  * @cfg {Boolean} streetViewControl default false
25852  * @cfg {Number} radius default 0
25853  * @cfg {String} locationName
25854  * @cfg {Boolean} draggable default true
25855  * @cfg {Boolean} enableAutocomplete default false
25856  * @cfg {Boolean} enableReverseGeocode default true
25857  * @cfg {String} markerTitle
25858  * 
25859  * @constructor
25860  * Create a new LocationPicker
25861  * @param {Object} config The config object
25862  */
25863
25864
25865 Roo.bootstrap.LocationPicker = function(config){
25866     
25867     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25868     
25869     this.addEvents({
25870         /**
25871          * @event initial
25872          * Fires when the picker initialized.
25873          * @param {Roo.bootstrap.LocationPicker} this
25874          * @param {Google Location} location
25875          */
25876         initial : true,
25877         /**
25878          * @event positionchanged
25879          * Fires when the picker position changed.
25880          * @param {Roo.bootstrap.LocationPicker} this
25881          * @param {Google Location} location
25882          */
25883         positionchanged : true,
25884         /**
25885          * @event resize
25886          * Fires when the map resize.
25887          * @param {Roo.bootstrap.LocationPicker} this
25888          */
25889         resize : true,
25890         /**
25891          * @event show
25892          * Fires when the map show.
25893          * @param {Roo.bootstrap.LocationPicker} this
25894          */
25895         show : true,
25896         /**
25897          * @event hide
25898          * Fires when the map hide.
25899          * @param {Roo.bootstrap.LocationPicker} this
25900          */
25901         hide : true,
25902         /**
25903          * @event mapClick
25904          * Fires when click the map.
25905          * @param {Roo.bootstrap.LocationPicker} this
25906          * @param {Map event} e
25907          */
25908         mapClick : true,
25909         /**
25910          * @event mapRightClick
25911          * Fires when right click the map.
25912          * @param {Roo.bootstrap.LocationPicker} this
25913          * @param {Map event} e
25914          */
25915         mapRightClick : true,
25916         /**
25917          * @event markerClick
25918          * Fires when click the marker.
25919          * @param {Roo.bootstrap.LocationPicker} this
25920          * @param {Map event} e
25921          */
25922         markerClick : true,
25923         /**
25924          * @event markerRightClick
25925          * Fires when right click the marker.
25926          * @param {Roo.bootstrap.LocationPicker} this
25927          * @param {Map event} e
25928          */
25929         markerRightClick : true,
25930         /**
25931          * @event OverlayViewDraw
25932          * Fires when OverlayView Draw
25933          * @param {Roo.bootstrap.LocationPicker} this
25934          */
25935         OverlayViewDraw : true,
25936         /**
25937          * @event OverlayViewOnAdd
25938          * Fires when OverlayView Draw
25939          * @param {Roo.bootstrap.LocationPicker} this
25940          */
25941         OverlayViewOnAdd : true,
25942         /**
25943          * @event OverlayViewOnRemove
25944          * Fires when OverlayView Draw
25945          * @param {Roo.bootstrap.LocationPicker} this
25946          */
25947         OverlayViewOnRemove : true,
25948         /**
25949          * @event OverlayViewShow
25950          * Fires when OverlayView Draw
25951          * @param {Roo.bootstrap.LocationPicker} this
25952          * @param {Pixel} cpx
25953          */
25954         OverlayViewShow : true,
25955         /**
25956          * @event OverlayViewHide
25957          * Fires when OverlayView Draw
25958          * @param {Roo.bootstrap.LocationPicker} this
25959          */
25960         OverlayViewHide : true,
25961         /**
25962          * @event loadexception
25963          * Fires when load google lib failed.
25964          * @param {Roo.bootstrap.LocationPicker} this
25965          */
25966         loadexception : true
25967     });
25968         
25969 };
25970
25971 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25972     
25973     gMapContext: false,
25974     
25975     latitude: 0,
25976     longitude: 0,
25977     zoom: 15,
25978     mapTypeId: false,
25979     mapTypeControl: false,
25980     disableDoubleClickZoom: false,
25981     scrollwheel: true,
25982     streetViewControl: false,
25983     radius: 0,
25984     locationName: '',
25985     draggable: true,
25986     enableAutocomplete: false,
25987     enableReverseGeocode: true,
25988     markerTitle: '',
25989     
25990     getAutoCreate: function()
25991     {
25992
25993         var cfg = {
25994             tag: 'div',
25995             cls: 'roo-location-picker'
25996         };
25997         
25998         return cfg
25999     },
26000     
26001     initEvents: function(ct, position)
26002     {       
26003         if(!this.el.getWidth() || this.isApplied()){
26004             return;
26005         }
26006         
26007         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26008         
26009         this.initial();
26010     },
26011     
26012     initial: function()
26013     {
26014         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26015             this.fireEvent('loadexception', this);
26016             return;
26017         }
26018         
26019         if(!this.mapTypeId){
26020             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26021         }
26022         
26023         this.gMapContext = this.GMapContext();
26024         
26025         this.initOverlayView();
26026         
26027         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26028         
26029         var _this = this;
26030                 
26031         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26032             _this.setPosition(_this.gMapContext.marker.position);
26033         });
26034         
26035         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26036             _this.fireEvent('mapClick', this, event);
26037             
26038         });
26039
26040         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26041             _this.fireEvent('mapRightClick', this, event);
26042             
26043         });
26044         
26045         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26046             _this.fireEvent('markerClick', this, event);
26047             
26048         });
26049
26050         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26051             _this.fireEvent('markerRightClick', this, event);
26052             
26053         });
26054         
26055         this.setPosition(this.gMapContext.location);
26056         
26057         this.fireEvent('initial', this, this.gMapContext.location);
26058     },
26059     
26060     initOverlayView: function()
26061     {
26062         var _this = this;
26063         
26064         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26065             
26066             draw: function()
26067             {
26068                 _this.fireEvent('OverlayViewDraw', _this);
26069             },
26070             
26071             onAdd: function()
26072             {
26073                 _this.fireEvent('OverlayViewOnAdd', _this);
26074             },
26075             
26076             onRemove: function()
26077             {
26078                 _this.fireEvent('OverlayViewOnRemove', _this);
26079             },
26080             
26081             show: function(cpx)
26082             {
26083                 _this.fireEvent('OverlayViewShow', _this, cpx);
26084             },
26085             
26086             hide: function()
26087             {
26088                 _this.fireEvent('OverlayViewHide', _this);
26089             }
26090             
26091         });
26092     },
26093     
26094     fromLatLngToContainerPixel: function(event)
26095     {
26096         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26097     },
26098     
26099     isApplied: function() 
26100     {
26101         return this.getGmapContext() == false ? false : true;
26102     },
26103     
26104     getGmapContext: function() 
26105     {
26106         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26107     },
26108     
26109     GMapContext: function() 
26110     {
26111         var position = new google.maps.LatLng(this.latitude, this.longitude);
26112         
26113         var _map = new google.maps.Map(this.el.dom, {
26114             center: position,
26115             zoom: this.zoom,
26116             mapTypeId: this.mapTypeId,
26117             mapTypeControl: this.mapTypeControl,
26118             disableDoubleClickZoom: this.disableDoubleClickZoom,
26119             scrollwheel: this.scrollwheel,
26120             streetViewControl: this.streetViewControl,
26121             locationName: this.locationName,
26122             draggable: this.draggable,
26123             enableAutocomplete: this.enableAutocomplete,
26124             enableReverseGeocode: this.enableReverseGeocode
26125         });
26126         
26127         var _marker = new google.maps.Marker({
26128             position: position,
26129             map: _map,
26130             title: this.markerTitle,
26131             draggable: this.draggable
26132         });
26133         
26134         return {
26135             map: _map,
26136             marker: _marker,
26137             circle: null,
26138             location: position,
26139             radius: this.radius,
26140             locationName: this.locationName,
26141             addressComponents: {
26142                 formatted_address: null,
26143                 addressLine1: null,
26144                 addressLine2: null,
26145                 streetName: null,
26146                 streetNumber: null,
26147                 city: null,
26148                 district: null,
26149                 state: null,
26150                 stateOrProvince: null
26151             },
26152             settings: this,
26153             domContainer: this.el.dom,
26154             geodecoder: new google.maps.Geocoder()
26155         };
26156     },
26157     
26158     drawCircle: function(center, radius, options) 
26159     {
26160         if (this.gMapContext.circle != null) {
26161             this.gMapContext.circle.setMap(null);
26162         }
26163         if (radius > 0) {
26164             radius *= 1;
26165             options = Roo.apply({}, options, {
26166                 strokeColor: "#0000FF",
26167                 strokeOpacity: .35,
26168                 strokeWeight: 2,
26169                 fillColor: "#0000FF",
26170                 fillOpacity: .2
26171             });
26172             
26173             options.map = this.gMapContext.map;
26174             options.radius = radius;
26175             options.center = center;
26176             this.gMapContext.circle = new google.maps.Circle(options);
26177             return this.gMapContext.circle;
26178         }
26179         
26180         return null;
26181     },
26182     
26183     setPosition: function(location) 
26184     {
26185         this.gMapContext.location = location;
26186         this.gMapContext.marker.setPosition(location);
26187         this.gMapContext.map.panTo(location);
26188         this.drawCircle(location, this.gMapContext.radius, {});
26189         
26190         var _this = this;
26191         
26192         if (this.gMapContext.settings.enableReverseGeocode) {
26193             this.gMapContext.geodecoder.geocode({
26194                 latLng: this.gMapContext.location
26195             }, function(results, status) {
26196                 
26197                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26198                     _this.gMapContext.locationName = results[0].formatted_address;
26199                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26200                     
26201                     _this.fireEvent('positionchanged', this, location);
26202                 }
26203             });
26204             
26205             return;
26206         }
26207         
26208         this.fireEvent('positionchanged', this, location);
26209     },
26210     
26211     resize: function()
26212     {
26213         google.maps.event.trigger(this.gMapContext.map, "resize");
26214         
26215         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26216         
26217         this.fireEvent('resize', this);
26218     },
26219     
26220     setPositionByLatLng: function(latitude, longitude)
26221     {
26222         this.setPosition(new google.maps.LatLng(latitude, longitude));
26223     },
26224     
26225     getCurrentPosition: function() 
26226     {
26227         return {
26228             latitude: this.gMapContext.location.lat(),
26229             longitude: this.gMapContext.location.lng()
26230         };
26231     },
26232     
26233     getAddressName: function() 
26234     {
26235         return this.gMapContext.locationName;
26236     },
26237     
26238     getAddressComponents: function() 
26239     {
26240         return this.gMapContext.addressComponents;
26241     },
26242     
26243     address_component_from_google_geocode: function(address_components) 
26244     {
26245         var result = {};
26246         
26247         for (var i = 0; i < address_components.length; i++) {
26248             var component = address_components[i];
26249             if (component.types.indexOf("postal_code") >= 0) {
26250                 result.postalCode = component.short_name;
26251             } else if (component.types.indexOf("street_number") >= 0) {
26252                 result.streetNumber = component.short_name;
26253             } else if (component.types.indexOf("route") >= 0) {
26254                 result.streetName = component.short_name;
26255             } else if (component.types.indexOf("neighborhood") >= 0) {
26256                 result.city = component.short_name;
26257             } else if (component.types.indexOf("locality") >= 0) {
26258                 result.city = component.short_name;
26259             } else if (component.types.indexOf("sublocality") >= 0) {
26260                 result.district = component.short_name;
26261             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26262                 result.stateOrProvince = component.short_name;
26263             } else if (component.types.indexOf("country") >= 0) {
26264                 result.country = component.short_name;
26265             }
26266         }
26267         
26268         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26269         result.addressLine2 = "";
26270         return result;
26271     },
26272     
26273     setZoomLevel: function(zoom)
26274     {
26275         this.gMapContext.map.setZoom(zoom);
26276     },
26277     
26278     show: function()
26279     {
26280         if(!this.el){
26281             return;
26282         }
26283         
26284         this.el.show();
26285         
26286         this.resize();
26287         
26288         this.fireEvent('show', this);
26289     },
26290     
26291     hide: function()
26292     {
26293         if(!this.el){
26294             return;
26295         }
26296         
26297         this.el.hide();
26298         
26299         this.fireEvent('hide', this);
26300     }
26301     
26302 });
26303
26304 Roo.apply(Roo.bootstrap.LocationPicker, {
26305     
26306     OverlayView : function(map, options)
26307     {
26308         options = options || {};
26309         
26310         this.setMap(map);
26311     }
26312     
26313     
26314 });/*
26315  * - LGPL
26316  *
26317  * Alert
26318  * 
26319  */
26320
26321 /**
26322  * @class Roo.bootstrap.Alert
26323  * @extends Roo.bootstrap.Component
26324  * Bootstrap Alert class
26325  * @cfg {String} title The title of alert
26326  * @cfg {String} html The content of alert
26327  * @cfg {String} weight (  success | info | warning | danger )
26328  * @cfg {String} faicon font-awesomeicon
26329  * 
26330  * @constructor
26331  * Create a new alert
26332  * @param {Object} config The config object
26333  */
26334
26335
26336 Roo.bootstrap.Alert = function(config){
26337     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26338     
26339 };
26340
26341 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26342     
26343     title: '',
26344     html: '',
26345     weight: false,
26346     faicon: false,
26347     
26348     getAutoCreate : function()
26349     {
26350         
26351         var cfg = {
26352             tag : 'div',
26353             cls : 'alert',
26354             cn : [
26355                 {
26356                     tag : 'i',
26357                     cls : 'roo-alert-icon'
26358                     
26359                 },
26360                 {
26361                     tag : 'b',
26362                     cls : 'roo-alert-title',
26363                     html : this.title
26364                 },
26365                 {
26366                     tag : 'span',
26367                     cls : 'roo-alert-text',
26368                     html : this.html
26369                 }
26370             ]
26371         };
26372         
26373         if(this.faicon){
26374             cfg.cn[0].cls += ' fa ' + this.faicon;
26375         }
26376         
26377         if(this.weight){
26378             cfg.cls += ' alert-' + this.weight;
26379         }
26380         
26381         return cfg;
26382     },
26383     
26384     initEvents: function() 
26385     {
26386         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26387     },
26388     
26389     setTitle : function(str)
26390     {
26391         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26392     },
26393     
26394     setText : function(str)
26395     {
26396         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26397     },
26398     
26399     setWeight : function(weight)
26400     {
26401         if(this.weight){
26402             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26403         }
26404         
26405         this.weight = weight;
26406         
26407         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26408     },
26409     
26410     setIcon : function(icon)
26411     {
26412         if(this.faicon){
26413             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26414         }
26415         
26416         this.faicon = icon;
26417         
26418         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26419     },
26420     
26421     hide: function() 
26422     {
26423         this.el.hide();   
26424     },
26425     
26426     show: function() 
26427     {  
26428         this.el.show();   
26429     }
26430     
26431 });
26432
26433  
26434 /*
26435 * Licence: LGPL
26436 */
26437
26438 /**
26439  * @class Roo.bootstrap.UploadCropbox
26440  * @extends Roo.bootstrap.Component
26441  * Bootstrap UploadCropbox class
26442  * @cfg {String} emptyText show when image has been loaded
26443  * @cfg {String} rotateNotify show when image too small to rotate
26444  * @cfg {Number} errorTimeout default 3000
26445  * @cfg {Number} minWidth default 300
26446  * @cfg {Number} minHeight default 300
26447  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26448  * @cfg {Boolean} isDocument (true|false) default false
26449  * @cfg {String} url action url
26450  * @cfg {String} paramName default 'imageUpload'
26451  * @cfg {String} method default POST
26452  * @cfg {Boolean} loadMask (true|false) default true
26453  * @cfg {Boolean} loadingText default 'Loading...'
26454  * 
26455  * @constructor
26456  * Create a new UploadCropbox
26457  * @param {Object} config The config object
26458  */
26459
26460 Roo.bootstrap.UploadCropbox = function(config){
26461     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26462     
26463     this.addEvents({
26464         /**
26465          * @event beforeselectfile
26466          * Fire before select file
26467          * @param {Roo.bootstrap.UploadCropbox} this
26468          */
26469         "beforeselectfile" : true,
26470         /**
26471          * @event initial
26472          * Fire after initEvent
26473          * @param {Roo.bootstrap.UploadCropbox} this
26474          */
26475         "initial" : true,
26476         /**
26477          * @event crop
26478          * Fire after initEvent
26479          * @param {Roo.bootstrap.UploadCropbox} this
26480          * @param {String} data
26481          */
26482         "crop" : true,
26483         /**
26484          * @event prepare
26485          * Fire when preparing the file data
26486          * @param {Roo.bootstrap.UploadCropbox} this
26487          * @param {Object} file
26488          */
26489         "prepare" : true,
26490         /**
26491          * @event exception
26492          * Fire when get exception
26493          * @param {Roo.bootstrap.UploadCropbox} this
26494          * @param {XMLHttpRequest} xhr
26495          */
26496         "exception" : true,
26497         /**
26498          * @event beforeloadcanvas
26499          * Fire before load the canvas
26500          * @param {Roo.bootstrap.UploadCropbox} this
26501          * @param {String} src
26502          */
26503         "beforeloadcanvas" : true,
26504         /**
26505          * @event trash
26506          * Fire when trash image
26507          * @param {Roo.bootstrap.UploadCropbox} this
26508          */
26509         "trash" : true,
26510         /**
26511          * @event download
26512          * Fire when download the image
26513          * @param {Roo.bootstrap.UploadCropbox} this
26514          */
26515         "download" : true,
26516         /**
26517          * @event footerbuttonclick
26518          * Fire when footerbuttonclick
26519          * @param {Roo.bootstrap.UploadCropbox} this
26520          * @param {String} type
26521          */
26522         "footerbuttonclick" : true,
26523         /**
26524          * @event resize
26525          * Fire when resize
26526          * @param {Roo.bootstrap.UploadCropbox} this
26527          */
26528         "resize" : true,
26529         /**
26530          * @event rotate
26531          * Fire when rotate the image
26532          * @param {Roo.bootstrap.UploadCropbox} this
26533          * @param {String} pos
26534          */
26535         "rotate" : true,
26536         /**
26537          * @event inspect
26538          * Fire when inspect the file
26539          * @param {Roo.bootstrap.UploadCropbox} this
26540          * @param {Object} file
26541          */
26542         "inspect" : true,
26543         /**
26544          * @event upload
26545          * Fire when xhr upload the file
26546          * @param {Roo.bootstrap.UploadCropbox} this
26547          * @param {Object} data
26548          */
26549         "upload" : true,
26550         /**
26551          * @event arrange
26552          * Fire when arrange the file data
26553          * @param {Roo.bootstrap.UploadCropbox} this
26554          * @param {Object} formData
26555          */
26556         "arrange" : true
26557     });
26558     
26559     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26560 };
26561
26562 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26563     
26564     emptyText : 'Click to upload image',
26565     rotateNotify : 'Image is too small to rotate',
26566     errorTimeout : 3000,
26567     scale : 0,
26568     baseScale : 1,
26569     rotate : 0,
26570     dragable : false,
26571     pinching : false,
26572     mouseX : 0,
26573     mouseY : 0,
26574     cropData : false,
26575     minWidth : 300,
26576     minHeight : 300,
26577     file : false,
26578     exif : {},
26579     baseRotate : 1,
26580     cropType : 'image/jpeg',
26581     buttons : false,
26582     canvasLoaded : false,
26583     isDocument : false,
26584     method : 'POST',
26585     paramName : 'imageUpload',
26586     loadMask : true,
26587     loadingText : 'Loading...',
26588     maskEl : false,
26589     
26590     getAutoCreate : function()
26591     {
26592         var cfg = {
26593             tag : 'div',
26594             cls : 'roo-upload-cropbox',
26595             cn : [
26596                 {
26597                     tag : 'input',
26598                     cls : 'roo-upload-cropbox-selector',
26599                     type : 'file'
26600                 },
26601                 {
26602                     tag : 'div',
26603                     cls : 'roo-upload-cropbox-body',
26604                     style : 'cursor:pointer',
26605                     cn : [
26606                         {
26607                             tag : 'div',
26608                             cls : 'roo-upload-cropbox-preview'
26609                         },
26610                         {
26611                             tag : 'div',
26612                             cls : 'roo-upload-cropbox-thumb'
26613                         },
26614                         {
26615                             tag : 'div',
26616                             cls : 'roo-upload-cropbox-empty-notify',
26617                             html : this.emptyText
26618                         },
26619                         {
26620                             tag : 'div',
26621                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26622                             html : this.rotateNotify
26623                         }
26624                     ]
26625                 },
26626                 {
26627                     tag : 'div',
26628                     cls : 'roo-upload-cropbox-footer',
26629                     cn : {
26630                         tag : 'div',
26631                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26632                         cn : []
26633                     }
26634                 }
26635             ]
26636         };
26637         
26638         return cfg;
26639     },
26640     
26641     onRender : function(ct, position)
26642     {
26643         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26644         
26645         if (this.buttons.length) {
26646             
26647             Roo.each(this.buttons, function(bb) {
26648                 
26649                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26650                 
26651                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26652                 
26653             }, this);
26654         }
26655         
26656         if(this.loadMask){
26657             this.maskEl = this.el;
26658         }
26659     },
26660     
26661     initEvents : function()
26662     {
26663         this.urlAPI = (window.createObjectURL && window) || 
26664                                 (window.URL && URL.revokeObjectURL && URL) || 
26665                                 (window.webkitURL && webkitURL);
26666                         
26667         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26668         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26669         
26670         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26671         this.selectorEl.hide();
26672         
26673         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26674         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26675         
26676         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26677         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26678         this.thumbEl.hide();
26679         
26680         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26681         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26682         
26683         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26684         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26685         this.errorEl.hide();
26686         
26687         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26688         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26689         this.footerEl.hide();
26690         
26691         this.setThumbBoxSize();
26692         
26693         this.bind();
26694         
26695         this.resize();
26696         
26697         this.fireEvent('initial', this);
26698     },
26699
26700     bind : function()
26701     {
26702         var _this = this;
26703         
26704         window.addEventListener("resize", function() { _this.resize(); } );
26705         
26706         this.bodyEl.on('click', this.beforeSelectFile, this);
26707         
26708         if(Roo.isTouch){
26709             this.bodyEl.on('touchstart', this.onTouchStart, this);
26710             this.bodyEl.on('touchmove', this.onTouchMove, this);
26711             this.bodyEl.on('touchend', this.onTouchEnd, this);
26712         }
26713         
26714         if(!Roo.isTouch){
26715             this.bodyEl.on('mousedown', this.onMouseDown, this);
26716             this.bodyEl.on('mousemove', this.onMouseMove, this);
26717             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26718             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26719             Roo.get(document).on('mouseup', this.onMouseUp, this);
26720         }
26721         
26722         this.selectorEl.on('change', this.onFileSelected, this);
26723     },
26724     
26725     reset : function()
26726     {    
26727         this.scale = 0;
26728         this.baseScale = 1;
26729         this.rotate = 0;
26730         this.baseRotate = 1;
26731         this.dragable = false;
26732         this.pinching = false;
26733         this.mouseX = 0;
26734         this.mouseY = 0;
26735         this.cropData = false;
26736         this.notifyEl.dom.innerHTML = this.emptyText;
26737         
26738         this.selectorEl.dom.value = '';
26739         
26740     },
26741     
26742     resize : function()
26743     {
26744         if(this.fireEvent('resize', this) != false){
26745             this.setThumbBoxPosition();
26746             this.setCanvasPosition();
26747         }
26748     },
26749     
26750     onFooterButtonClick : function(e, el, o, type)
26751     {
26752         switch (type) {
26753             case 'rotate-left' :
26754                 this.onRotateLeft(e);
26755                 break;
26756             case 'rotate-right' :
26757                 this.onRotateRight(e);
26758                 break;
26759             case 'picture' :
26760                 this.beforeSelectFile(e);
26761                 break;
26762             case 'trash' :
26763                 this.trash(e);
26764                 break;
26765             case 'crop' :
26766                 this.crop(e);
26767                 break;
26768             case 'download' :
26769                 this.download(e);
26770                 break;
26771             default :
26772                 break;
26773         }
26774         
26775         this.fireEvent('footerbuttonclick', this, type);
26776     },
26777     
26778     beforeSelectFile : function(e)
26779     {
26780         e.preventDefault();
26781         
26782         if(this.fireEvent('beforeselectfile', this) != false){
26783             this.selectorEl.dom.click();
26784         }
26785     },
26786     
26787     onFileSelected : function(e)
26788     {
26789         e.preventDefault();
26790         
26791         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26792             return;
26793         }
26794         
26795         var file = this.selectorEl.dom.files[0];
26796         
26797         if(this.fireEvent('inspect', this, file) != false){
26798             this.prepare(file);
26799         }
26800         
26801     },
26802     
26803     trash : function(e)
26804     {
26805         this.fireEvent('trash', this);
26806     },
26807     
26808     download : function(e)
26809     {
26810         this.fireEvent('download', this);
26811     },
26812     
26813     loadCanvas : function(src)
26814     {   
26815         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26816             
26817             this.reset();
26818             
26819             this.imageEl = document.createElement('img');
26820             
26821             var _this = this;
26822             
26823             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26824             
26825             this.imageEl.src = src;
26826         }
26827     },
26828     
26829     onLoadCanvas : function()
26830     {   
26831         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26832         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26833         
26834         this.bodyEl.un('click', this.beforeSelectFile, this);
26835         
26836         this.notifyEl.hide();
26837         this.thumbEl.show();
26838         this.footerEl.show();
26839         
26840         this.baseRotateLevel();
26841         
26842         if(this.isDocument){
26843             this.setThumbBoxSize();
26844         }
26845         
26846         this.setThumbBoxPosition();
26847         
26848         this.baseScaleLevel();
26849         
26850         this.draw();
26851         
26852         this.resize();
26853         
26854         this.canvasLoaded = true;
26855         
26856         if(this.loadMask){
26857             this.maskEl.unmask();
26858         }
26859         
26860     },
26861     
26862     setCanvasPosition : function()
26863     {   
26864         if(!this.canvasEl){
26865             return;
26866         }
26867         
26868         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26869         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26870         
26871         this.previewEl.setLeft(pw);
26872         this.previewEl.setTop(ph);
26873         
26874     },
26875     
26876     onMouseDown : function(e)
26877     {   
26878         e.stopEvent();
26879         
26880         this.dragable = true;
26881         this.pinching = false;
26882         
26883         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26884             this.dragable = false;
26885             return;
26886         }
26887         
26888         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26889         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26890         
26891     },
26892     
26893     onMouseMove : function(e)
26894     {   
26895         e.stopEvent();
26896         
26897         if(!this.canvasLoaded){
26898             return;
26899         }
26900         
26901         if (!this.dragable){
26902             return;
26903         }
26904         
26905         var minX = Math.ceil(this.thumbEl.getLeft(true));
26906         var minY = Math.ceil(this.thumbEl.getTop(true));
26907         
26908         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26909         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26910         
26911         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26912         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26913         
26914         x = x - this.mouseX;
26915         y = y - this.mouseY;
26916         
26917         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26918         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26919         
26920         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26921         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26922         
26923         this.previewEl.setLeft(bgX);
26924         this.previewEl.setTop(bgY);
26925         
26926         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26927         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26928     },
26929     
26930     onMouseUp : function(e)
26931     {   
26932         e.stopEvent();
26933         
26934         this.dragable = false;
26935     },
26936     
26937     onMouseWheel : function(e)
26938     {   
26939         e.stopEvent();
26940         
26941         this.startScale = this.scale;
26942         
26943         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26944         
26945         if(!this.zoomable()){
26946             this.scale = this.startScale;
26947             return;
26948         }
26949         
26950         this.draw();
26951         
26952         return;
26953     },
26954     
26955     zoomable : function()
26956     {
26957         var minScale = this.thumbEl.getWidth() / this.minWidth;
26958         
26959         if(this.minWidth < this.minHeight){
26960             minScale = this.thumbEl.getHeight() / this.minHeight;
26961         }
26962         
26963         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26964         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26965         
26966         if(
26967                 this.isDocument &&
26968                 (this.rotate == 0 || this.rotate == 180) && 
26969                 (
26970                     width > this.imageEl.OriginWidth || 
26971                     height > this.imageEl.OriginHeight ||
26972                     (width < this.minWidth && height < this.minHeight)
26973                 )
26974         ){
26975             return false;
26976         }
26977         
26978         if(
26979                 this.isDocument &&
26980                 (this.rotate == 90 || this.rotate == 270) && 
26981                 (
26982                     width > this.imageEl.OriginWidth || 
26983                     height > this.imageEl.OriginHeight ||
26984                     (width < this.minHeight && height < this.minWidth)
26985                 )
26986         ){
26987             return false;
26988         }
26989         
26990         if(
26991                 !this.isDocument &&
26992                 (this.rotate == 0 || this.rotate == 180) && 
26993                 (
26994                     width < this.minWidth || 
26995                     width > this.imageEl.OriginWidth || 
26996                     height < this.minHeight || 
26997                     height > this.imageEl.OriginHeight
26998                 )
26999         ){
27000             return false;
27001         }
27002         
27003         if(
27004                 !this.isDocument &&
27005                 (this.rotate == 90 || this.rotate == 270) && 
27006                 (
27007                     width < this.minHeight || 
27008                     width > this.imageEl.OriginWidth || 
27009                     height < this.minWidth || 
27010                     height > this.imageEl.OriginHeight
27011                 )
27012         ){
27013             return false;
27014         }
27015         
27016         return true;
27017         
27018     },
27019     
27020     onRotateLeft : function(e)
27021     {   
27022         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27023             
27024             var minScale = this.thumbEl.getWidth() / this.minWidth;
27025             
27026             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27027             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27028             
27029             this.startScale = this.scale;
27030             
27031             while (this.getScaleLevel() < minScale){
27032             
27033                 this.scale = this.scale + 1;
27034                 
27035                 if(!this.zoomable()){
27036                     break;
27037                 }
27038                 
27039                 if(
27040                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27041                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27042                 ){
27043                     continue;
27044                 }
27045                 
27046                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27047
27048                 this.draw();
27049                 
27050                 return;
27051             }
27052             
27053             this.scale = this.startScale;
27054             
27055             this.onRotateFail();
27056             
27057             return false;
27058         }
27059         
27060         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27061
27062         if(this.isDocument){
27063             this.setThumbBoxSize();
27064             this.setThumbBoxPosition();
27065             this.setCanvasPosition();
27066         }
27067         
27068         this.draw();
27069         
27070         this.fireEvent('rotate', this, 'left');
27071         
27072     },
27073     
27074     onRotateRight : function(e)
27075     {
27076         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27077             
27078             var minScale = this.thumbEl.getWidth() / this.minWidth;
27079         
27080             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27081             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27082             
27083             this.startScale = this.scale;
27084             
27085             while (this.getScaleLevel() < minScale){
27086             
27087                 this.scale = this.scale + 1;
27088                 
27089                 if(!this.zoomable()){
27090                     break;
27091                 }
27092                 
27093                 if(
27094                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27095                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27096                 ){
27097                     continue;
27098                 }
27099                 
27100                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27101
27102                 this.draw();
27103                 
27104                 return;
27105             }
27106             
27107             this.scale = this.startScale;
27108             
27109             this.onRotateFail();
27110             
27111             return false;
27112         }
27113         
27114         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27115
27116         if(this.isDocument){
27117             this.setThumbBoxSize();
27118             this.setThumbBoxPosition();
27119             this.setCanvasPosition();
27120         }
27121         
27122         this.draw();
27123         
27124         this.fireEvent('rotate', this, 'right');
27125     },
27126     
27127     onRotateFail : function()
27128     {
27129         this.errorEl.show(true);
27130         
27131         var _this = this;
27132         
27133         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27134     },
27135     
27136     draw : function()
27137     {
27138         this.previewEl.dom.innerHTML = '';
27139         
27140         var canvasEl = document.createElement("canvas");
27141         
27142         var contextEl = canvasEl.getContext("2d");
27143         
27144         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27145         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27146         var center = this.imageEl.OriginWidth / 2;
27147         
27148         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27149             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27150             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27151             center = this.imageEl.OriginHeight / 2;
27152         }
27153         
27154         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27155         
27156         contextEl.translate(center, center);
27157         contextEl.rotate(this.rotate * Math.PI / 180);
27158
27159         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27160         
27161         this.canvasEl = document.createElement("canvas");
27162         
27163         this.contextEl = this.canvasEl.getContext("2d");
27164         
27165         switch (this.rotate) {
27166             case 0 :
27167                 
27168                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27169                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27170                 
27171                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27172                 
27173                 break;
27174             case 90 : 
27175                 
27176                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27177                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27178                 
27179                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27180                     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);
27181                     break;
27182                 }
27183                 
27184                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27185                 
27186                 break;
27187             case 180 :
27188                 
27189                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27190                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27191                 
27192                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27193                     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);
27194                     break;
27195                 }
27196                 
27197                 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);
27198                 
27199                 break;
27200             case 270 :
27201                 
27202                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27203                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27204         
27205                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27206                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27207                     break;
27208                 }
27209                 
27210                 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);
27211                 
27212                 break;
27213             default : 
27214                 break;
27215         }
27216         
27217         this.previewEl.appendChild(this.canvasEl);
27218         
27219         this.setCanvasPosition();
27220     },
27221     
27222     crop : function()
27223     {
27224         if(!this.canvasLoaded){
27225             return;
27226         }
27227         
27228         var imageCanvas = document.createElement("canvas");
27229         
27230         var imageContext = imageCanvas.getContext("2d");
27231         
27232         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27233         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27234         
27235         var center = imageCanvas.width / 2;
27236         
27237         imageContext.translate(center, center);
27238         
27239         imageContext.rotate(this.rotate * Math.PI / 180);
27240         
27241         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27242         
27243         var canvas = document.createElement("canvas");
27244         
27245         var context = canvas.getContext("2d");
27246                 
27247         canvas.width = this.minWidth;
27248         canvas.height = this.minHeight;
27249
27250         switch (this.rotate) {
27251             case 0 :
27252                 
27253                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27254                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27255                 
27256                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27257                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27258                 
27259                 var targetWidth = this.minWidth - 2 * x;
27260                 var targetHeight = this.minHeight - 2 * y;
27261                 
27262                 var scale = 1;
27263                 
27264                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27265                     scale = targetWidth / width;
27266                 }
27267                 
27268                 if(x > 0 && y == 0){
27269                     scale = targetHeight / height;
27270                 }
27271                 
27272                 if(x > 0 && y > 0){
27273                     scale = targetWidth / width;
27274                     
27275                     if(width < height){
27276                         scale = targetHeight / height;
27277                     }
27278                 }
27279                 
27280                 context.scale(scale, scale);
27281                 
27282                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27283                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27284
27285                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27286                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27287
27288                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27289                 
27290                 break;
27291             case 90 : 
27292                 
27293                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27294                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27295                 
27296                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27297                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27298                 
27299                 var targetWidth = this.minWidth - 2 * x;
27300                 var targetHeight = this.minHeight - 2 * y;
27301                 
27302                 var scale = 1;
27303                 
27304                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27305                     scale = targetWidth / width;
27306                 }
27307                 
27308                 if(x > 0 && y == 0){
27309                     scale = targetHeight / height;
27310                 }
27311                 
27312                 if(x > 0 && y > 0){
27313                     scale = targetWidth / width;
27314                     
27315                     if(width < height){
27316                         scale = targetHeight / height;
27317                     }
27318                 }
27319                 
27320                 context.scale(scale, scale);
27321                 
27322                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27323                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27324
27325                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27326                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27327                 
27328                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27329                 
27330                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27331                 
27332                 break;
27333             case 180 :
27334                 
27335                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27336                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27337                 
27338                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27339                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27340                 
27341                 var targetWidth = this.minWidth - 2 * x;
27342                 var targetHeight = this.minHeight - 2 * y;
27343                 
27344                 var scale = 1;
27345                 
27346                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27347                     scale = targetWidth / width;
27348                 }
27349                 
27350                 if(x > 0 && y == 0){
27351                     scale = targetHeight / height;
27352                 }
27353                 
27354                 if(x > 0 && y > 0){
27355                     scale = targetWidth / width;
27356                     
27357                     if(width < height){
27358                         scale = targetHeight / height;
27359                     }
27360                 }
27361                 
27362                 context.scale(scale, scale);
27363                 
27364                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27365                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27366
27367                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27368                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27369
27370                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27371                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27372                 
27373                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27374                 
27375                 break;
27376             case 270 :
27377                 
27378                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27379                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27380                 
27381                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27382                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27383                 
27384                 var targetWidth = this.minWidth - 2 * x;
27385                 var targetHeight = this.minHeight - 2 * y;
27386                 
27387                 var scale = 1;
27388                 
27389                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27390                     scale = targetWidth / width;
27391                 }
27392                 
27393                 if(x > 0 && y == 0){
27394                     scale = targetHeight / height;
27395                 }
27396                 
27397                 if(x > 0 && y > 0){
27398                     scale = targetWidth / width;
27399                     
27400                     if(width < height){
27401                         scale = targetHeight / height;
27402                     }
27403                 }
27404                 
27405                 context.scale(scale, scale);
27406                 
27407                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27408                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27409
27410                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27411                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27412                 
27413                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27414                 
27415                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27416                 
27417                 break;
27418             default : 
27419                 break;
27420         }
27421         
27422         this.cropData = canvas.toDataURL(this.cropType);
27423         
27424         if(this.fireEvent('crop', this, this.cropData) !== false){
27425             this.process(this.file, this.cropData);
27426         }
27427         
27428         return;
27429         
27430     },
27431     
27432     setThumbBoxSize : function()
27433     {
27434         var width, height;
27435         
27436         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27437             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27438             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27439             
27440             this.minWidth = width;
27441             this.minHeight = height;
27442             
27443             if(this.rotate == 90 || this.rotate == 270){
27444                 this.minWidth = height;
27445                 this.minHeight = width;
27446             }
27447         }
27448         
27449         height = 300;
27450         width = Math.ceil(this.minWidth * height / this.minHeight);
27451         
27452         if(this.minWidth > this.minHeight){
27453             width = 300;
27454             height = Math.ceil(this.minHeight * width / this.minWidth);
27455         }
27456         
27457         this.thumbEl.setStyle({
27458             width : width + 'px',
27459             height : height + 'px'
27460         });
27461
27462         return;
27463             
27464     },
27465     
27466     setThumbBoxPosition : function()
27467     {
27468         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27469         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27470         
27471         this.thumbEl.setLeft(x);
27472         this.thumbEl.setTop(y);
27473         
27474     },
27475     
27476     baseRotateLevel : function()
27477     {
27478         this.baseRotate = 1;
27479         
27480         if(
27481                 typeof(this.exif) != 'undefined' &&
27482                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27483                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27484         ){
27485             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27486         }
27487         
27488         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27489         
27490     },
27491     
27492     baseScaleLevel : function()
27493     {
27494         var width, height;
27495         
27496         if(this.isDocument){
27497             
27498             if(this.baseRotate == 6 || this.baseRotate == 8){
27499             
27500                 height = this.thumbEl.getHeight();
27501                 this.baseScale = height / this.imageEl.OriginWidth;
27502
27503                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27504                     width = this.thumbEl.getWidth();
27505                     this.baseScale = width / this.imageEl.OriginHeight;
27506                 }
27507
27508                 return;
27509             }
27510
27511             height = this.thumbEl.getHeight();
27512             this.baseScale = height / this.imageEl.OriginHeight;
27513
27514             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27515                 width = this.thumbEl.getWidth();
27516                 this.baseScale = width / this.imageEl.OriginWidth;
27517             }
27518
27519             return;
27520         }
27521         
27522         if(this.baseRotate == 6 || this.baseRotate == 8){
27523             
27524             width = this.thumbEl.getHeight();
27525             this.baseScale = width / this.imageEl.OriginHeight;
27526             
27527             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27528                 height = this.thumbEl.getWidth();
27529                 this.baseScale = height / this.imageEl.OriginHeight;
27530             }
27531             
27532             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27533                 height = this.thumbEl.getWidth();
27534                 this.baseScale = height / this.imageEl.OriginHeight;
27535                 
27536                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27537                     width = this.thumbEl.getHeight();
27538                     this.baseScale = width / this.imageEl.OriginWidth;
27539                 }
27540             }
27541             
27542             return;
27543         }
27544         
27545         width = this.thumbEl.getWidth();
27546         this.baseScale = width / this.imageEl.OriginWidth;
27547         
27548         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27549             height = this.thumbEl.getHeight();
27550             this.baseScale = height / this.imageEl.OriginHeight;
27551         }
27552         
27553         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27554             
27555             height = this.thumbEl.getHeight();
27556             this.baseScale = height / this.imageEl.OriginHeight;
27557             
27558             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27559                 width = this.thumbEl.getWidth();
27560                 this.baseScale = width / this.imageEl.OriginWidth;
27561             }
27562             
27563         }
27564         
27565         return;
27566     },
27567     
27568     getScaleLevel : function()
27569     {
27570         return this.baseScale * Math.pow(1.1, this.scale);
27571     },
27572     
27573     onTouchStart : function(e)
27574     {
27575         if(!this.canvasLoaded){
27576             this.beforeSelectFile(e);
27577             return;
27578         }
27579         
27580         var touches = e.browserEvent.touches;
27581         
27582         if(!touches){
27583             return;
27584         }
27585         
27586         if(touches.length == 1){
27587             this.onMouseDown(e);
27588             return;
27589         }
27590         
27591         if(touches.length != 2){
27592             return;
27593         }
27594         
27595         var coords = [];
27596         
27597         for(var i = 0, finger; finger = touches[i]; i++){
27598             coords.push(finger.pageX, finger.pageY);
27599         }
27600         
27601         var x = Math.pow(coords[0] - coords[2], 2);
27602         var y = Math.pow(coords[1] - coords[3], 2);
27603         
27604         this.startDistance = Math.sqrt(x + y);
27605         
27606         this.startScale = this.scale;
27607         
27608         this.pinching = true;
27609         this.dragable = false;
27610         
27611     },
27612     
27613     onTouchMove : function(e)
27614     {
27615         if(!this.pinching && !this.dragable){
27616             return;
27617         }
27618         
27619         var touches = e.browserEvent.touches;
27620         
27621         if(!touches){
27622             return;
27623         }
27624         
27625         if(this.dragable){
27626             this.onMouseMove(e);
27627             return;
27628         }
27629         
27630         var coords = [];
27631         
27632         for(var i = 0, finger; finger = touches[i]; i++){
27633             coords.push(finger.pageX, finger.pageY);
27634         }
27635         
27636         var x = Math.pow(coords[0] - coords[2], 2);
27637         var y = Math.pow(coords[1] - coords[3], 2);
27638         
27639         this.endDistance = Math.sqrt(x + y);
27640         
27641         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27642         
27643         if(!this.zoomable()){
27644             this.scale = this.startScale;
27645             return;
27646         }
27647         
27648         this.draw();
27649         
27650     },
27651     
27652     onTouchEnd : function(e)
27653     {
27654         this.pinching = false;
27655         this.dragable = false;
27656         
27657     },
27658     
27659     process : function(file, crop)
27660     {
27661         if(this.loadMask){
27662             this.maskEl.mask(this.loadingText);
27663         }
27664         
27665         this.xhr = new XMLHttpRequest();
27666         
27667         file.xhr = this.xhr;
27668
27669         this.xhr.open(this.method, this.url, true);
27670         
27671         var headers = {
27672             "Accept": "application/json",
27673             "Cache-Control": "no-cache",
27674             "X-Requested-With": "XMLHttpRequest"
27675         };
27676         
27677         for (var headerName in headers) {
27678             var headerValue = headers[headerName];
27679             if (headerValue) {
27680                 this.xhr.setRequestHeader(headerName, headerValue);
27681             }
27682         }
27683         
27684         var _this = this;
27685         
27686         this.xhr.onload = function()
27687         {
27688             _this.xhrOnLoad(_this.xhr);
27689         }
27690         
27691         this.xhr.onerror = function()
27692         {
27693             _this.xhrOnError(_this.xhr);
27694         }
27695         
27696         var formData = new FormData();
27697
27698         formData.append('returnHTML', 'NO');
27699         
27700         if(crop){
27701             formData.append('crop', crop);
27702         }
27703         
27704         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27705             formData.append(this.paramName, file, file.name);
27706         }
27707         
27708         if(typeof(file.filename) != 'undefined'){
27709             formData.append('filename', file.filename);
27710         }
27711         
27712         if(typeof(file.mimetype) != 'undefined'){
27713             formData.append('mimetype', file.mimetype);
27714         }
27715         
27716         if(this.fireEvent('arrange', this, formData) != false){
27717             this.xhr.send(formData);
27718         };
27719     },
27720     
27721     xhrOnLoad : function(xhr)
27722     {
27723         if(this.loadMask){
27724             this.maskEl.unmask();
27725         }
27726         
27727         if (xhr.readyState !== 4) {
27728             this.fireEvent('exception', this, xhr);
27729             return;
27730         }
27731
27732         var response = Roo.decode(xhr.responseText);
27733         
27734         if(!response.success){
27735             this.fireEvent('exception', this, xhr);
27736             return;
27737         }
27738         
27739         var response = Roo.decode(xhr.responseText);
27740         
27741         this.fireEvent('upload', this, response);
27742         
27743     },
27744     
27745     xhrOnError : function()
27746     {
27747         if(this.loadMask){
27748             this.maskEl.unmask();
27749         }
27750         
27751         Roo.log('xhr on error');
27752         
27753         var response = Roo.decode(xhr.responseText);
27754           
27755         Roo.log(response);
27756         
27757     },
27758     
27759     prepare : function(file)
27760     {   
27761         if(this.loadMask){
27762             this.maskEl.mask(this.loadingText);
27763         }
27764         
27765         this.file = false;
27766         this.exif = {};
27767         
27768         if(typeof(file) === 'string'){
27769             this.loadCanvas(file);
27770             return;
27771         }
27772         
27773         if(!file || !this.urlAPI){
27774             return;
27775         }
27776         
27777         this.file = file;
27778         this.cropType = file.type;
27779         
27780         var _this = this;
27781         
27782         if(this.fireEvent('prepare', this, this.file) != false){
27783             
27784             var reader = new FileReader();
27785             
27786             reader.onload = function (e) {
27787                 if (e.target.error) {
27788                     Roo.log(e.target.error);
27789                     return;
27790                 }
27791                 
27792                 var buffer = e.target.result,
27793                     dataView = new DataView(buffer),
27794                     offset = 2,
27795                     maxOffset = dataView.byteLength - 4,
27796                     markerBytes,
27797                     markerLength;
27798                 
27799                 if (dataView.getUint16(0) === 0xffd8) {
27800                     while (offset < maxOffset) {
27801                         markerBytes = dataView.getUint16(offset);
27802                         
27803                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27804                             markerLength = dataView.getUint16(offset + 2) + 2;
27805                             if (offset + markerLength > dataView.byteLength) {
27806                                 Roo.log('Invalid meta data: Invalid segment size.');
27807                                 break;
27808                             }
27809                             
27810                             if(markerBytes == 0xffe1){
27811                                 _this.parseExifData(
27812                                     dataView,
27813                                     offset,
27814                                     markerLength
27815                                 );
27816                             }
27817                             
27818                             offset += markerLength;
27819                             
27820                             continue;
27821                         }
27822                         
27823                         break;
27824                     }
27825                     
27826                 }
27827                 
27828                 var url = _this.urlAPI.createObjectURL(_this.file);
27829                 
27830                 _this.loadCanvas(url);
27831                 
27832                 return;
27833             }
27834             
27835             reader.readAsArrayBuffer(this.file);
27836             
27837         }
27838         
27839     },
27840     
27841     parseExifData : function(dataView, offset, length)
27842     {
27843         var tiffOffset = offset + 10,
27844             littleEndian,
27845             dirOffset;
27846     
27847         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27848             // No Exif data, might be XMP data instead
27849             return;
27850         }
27851         
27852         // Check for the ASCII code for "Exif" (0x45786966):
27853         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27854             // No Exif data, might be XMP data instead
27855             return;
27856         }
27857         if (tiffOffset + 8 > dataView.byteLength) {
27858             Roo.log('Invalid Exif data: Invalid segment size.');
27859             return;
27860         }
27861         // Check for the two null bytes:
27862         if (dataView.getUint16(offset + 8) !== 0x0000) {
27863             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27864             return;
27865         }
27866         // Check the byte alignment:
27867         switch (dataView.getUint16(tiffOffset)) {
27868         case 0x4949:
27869             littleEndian = true;
27870             break;
27871         case 0x4D4D:
27872             littleEndian = false;
27873             break;
27874         default:
27875             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27876             return;
27877         }
27878         // Check for the TIFF tag marker (0x002A):
27879         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27880             Roo.log('Invalid Exif data: Missing TIFF marker.');
27881             return;
27882         }
27883         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27884         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27885         
27886         this.parseExifTags(
27887             dataView,
27888             tiffOffset,
27889             tiffOffset + dirOffset,
27890             littleEndian
27891         );
27892     },
27893     
27894     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27895     {
27896         var tagsNumber,
27897             dirEndOffset,
27898             i;
27899         if (dirOffset + 6 > dataView.byteLength) {
27900             Roo.log('Invalid Exif data: Invalid directory offset.');
27901             return;
27902         }
27903         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27904         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27905         if (dirEndOffset + 4 > dataView.byteLength) {
27906             Roo.log('Invalid Exif data: Invalid directory size.');
27907             return;
27908         }
27909         for (i = 0; i < tagsNumber; i += 1) {
27910             this.parseExifTag(
27911                 dataView,
27912                 tiffOffset,
27913                 dirOffset + 2 + 12 * i, // tag offset
27914                 littleEndian
27915             );
27916         }
27917         // Return the offset to the next directory:
27918         return dataView.getUint32(dirEndOffset, littleEndian);
27919     },
27920     
27921     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27922     {
27923         var tag = dataView.getUint16(offset, littleEndian);
27924         
27925         this.exif[tag] = this.getExifValue(
27926             dataView,
27927             tiffOffset,
27928             offset,
27929             dataView.getUint16(offset + 2, littleEndian), // tag type
27930             dataView.getUint32(offset + 4, littleEndian), // tag length
27931             littleEndian
27932         );
27933     },
27934     
27935     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27936     {
27937         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27938             tagSize,
27939             dataOffset,
27940             values,
27941             i,
27942             str,
27943             c;
27944     
27945         if (!tagType) {
27946             Roo.log('Invalid Exif data: Invalid tag type.');
27947             return;
27948         }
27949         
27950         tagSize = tagType.size * length;
27951         // Determine if the value is contained in the dataOffset bytes,
27952         // or if the value at the dataOffset is a pointer to the actual data:
27953         dataOffset = tagSize > 4 ?
27954                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27955         if (dataOffset + tagSize > dataView.byteLength) {
27956             Roo.log('Invalid Exif data: Invalid data offset.');
27957             return;
27958         }
27959         if (length === 1) {
27960             return tagType.getValue(dataView, dataOffset, littleEndian);
27961         }
27962         values = [];
27963         for (i = 0; i < length; i += 1) {
27964             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27965         }
27966         
27967         if (tagType.ascii) {
27968             str = '';
27969             // Concatenate the chars:
27970             for (i = 0; i < values.length; i += 1) {
27971                 c = values[i];
27972                 // Ignore the terminating NULL byte(s):
27973                 if (c === '\u0000') {
27974                     break;
27975                 }
27976                 str += c;
27977             }
27978             return str;
27979         }
27980         return values;
27981     }
27982     
27983 });
27984
27985 Roo.apply(Roo.bootstrap.UploadCropbox, {
27986     tags : {
27987         'Orientation': 0x0112
27988     },
27989     
27990     Orientation: {
27991             1: 0, //'top-left',
27992 //            2: 'top-right',
27993             3: 180, //'bottom-right',
27994 //            4: 'bottom-left',
27995 //            5: 'left-top',
27996             6: 90, //'right-top',
27997 //            7: 'right-bottom',
27998             8: 270 //'left-bottom'
27999     },
28000     
28001     exifTagTypes : {
28002         // byte, 8-bit unsigned int:
28003         1: {
28004             getValue: function (dataView, dataOffset) {
28005                 return dataView.getUint8(dataOffset);
28006             },
28007             size: 1
28008         },
28009         // ascii, 8-bit byte:
28010         2: {
28011             getValue: function (dataView, dataOffset) {
28012                 return String.fromCharCode(dataView.getUint8(dataOffset));
28013             },
28014             size: 1,
28015             ascii: true
28016         },
28017         // short, 16 bit int:
28018         3: {
28019             getValue: function (dataView, dataOffset, littleEndian) {
28020                 return dataView.getUint16(dataOffset, littleEndian);
28021             },
28022             size: 2
28023         },
28024         // long, 32 bit int:
28025         4: {
28026             getValue: function (dataView, dataOffset, littleEndian) {
28027                 return dataView.getUint32(dataOffset, littleEndian);
28028             },
28029             size: 4
28030         },
28031         // rational = two long values, first is numerator, second is denominator:
28032         5: {
28033             getValue: function (dataView, dataOffset, littleEndian) {
28034                 return dataView.getUint32(dataOffset, littleEndian) /
28035                     dataView.getUint32(dataOffset + 4, littleEndian);
28036             },
28037             size: 8
28038         },
28039         // slong, 32 bit signed int:
28040         9: {
28041             getValue: function (dataView, dataOffset, littleEndian) {
28042                 return dataView.getInt32(dataOffset, littleEndian);
28043             },
28044             size: 4
28045         },
28046         // srational, two slongs, first is numerator, second is denominator:
28047         10: {
28048             getValue: function (dataView, dataOffset, littleEndian) {
28049                 return dataView.getInt32(dataOffset, littleEndian) /
28050                     dataView.getInt32(dataOffset + 4, littleEndian);
28051             },
28052             size: 8
28053         }
28054     },
28055     
28056     footer : {
28057         STANDARD : [
28058             {
28059                 tag : 'div',
28060                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28061                 action : 'rotate-left',
28062                 cn : [
28063                     {
28064                         tag : 'button',
28065                         cls : 'btn btn-default',
28066                         html : '<i class="fa fa-undo"></i>'
28067                     }
28068                 ]
28069             },
28070             {
28071                 tag : 'div',
28072                 cls : 'btn-group roo-upload-cropbox-picture',
28073                 action : 'picture',
28074                 cn : [
28075                     {
28076                         tag : 'button',
28077                         cls : 'btn btn-default',
28078                         html : '<i class="fa fa-picture-o"></i>'
28079                     }
28080                 ]
28081             },
28082             {
28083                 tag : 'div',
28084                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28085                 action : 'rotate-right',
28086                 cn : [
28087                     {
28088                         tag : 'button',
28089                         cls : 'btn btn-default',
28090                         html : '<i class="fa fa-repeat"></i>'
28091                     }
28092                 ]
28093             }
28094         ],
28095         DOCUMENT : [
28096             {
28097                 tag : 'div',
28098                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28099                 action : 'rotate-left',
28100                 cn : [
28101                     {
28102                         tag : 'button',
28103                         cls : 'btn btn-default',
28104                         html : '<i class="fa fa-undo"></i>'
28105                     }
28106                 ]
28107             },
28108             {
28109                 tag : 'div',
28110                 cls : 'btn-group roo-upload-cropbox-download',
28111                 action : 'download',
28112                 cn : [
28113                     {
28114                         tag : 'button',
28115                         cls : 'btn btn-default',
28116                         html : '<i class="fa fa-download"></i>'
28117                     }
28118                 ]
28119             },
28120             {
28121                 tag : 'div',
28122                 cls : 'btn-group roo-upload-cropbox-crop',
28123                 action : 'crop',
28124                 cn : [
28125                     {
28126                         tag : 'button',
28127                         cls : 'btn btn-default',
28128                         html : '<i class="fa fa-crop"></i>'
28129                     }
28130                 ]
28131             },
28132             {
28133                 tag : 'div',
28134                 cls : 'btn-group roo-upload-cropbox-trash',
28135                 action : 'trash',
28136                 cn : [
28137                     {
28138                         tag : 'button',
28139                         cls : 'btn btn-default',
28140                         html : '<i class="fa fa-trash"></i>'
28141                     }
28142                 ]
28143             },
28144             {
28145                 tag : 'div',
28146                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28147                 action : 'rotate-right',
28148                 cn : [
28149                     {
28150                         tag : 'button',
28151                         cls : 'btn btn-default',
28152                         html : '<i class="fa fa-repeat"></i>'
28153                     }
28154                 ]
28155             }
28156         ],
28157         ROTATOR : [
28158             {
28159                 tag : 'div',
28160                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28161                 action : 'rotate-left',
28162                 cn : [
28163                     {
28164                         tag : 'button',
28165                         cls : 'btn btn-default',
28166                         html : '<i class="fa fa-undo"></i>'
28167                     }
28168                 ]
28169             },
28170             {
28171                 tag : 'div',
28172                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28173                 action : 'rotate-right',
28174                 cn : [
28175                     {
28176                         tag : 'button',
28177                         cls : 'btn btn-default',
28178                         html : '<i class="fa fa-repeat"></i>'
28179                     }
28180                 ]
28181             }
28182         ]
28183     }
28184 });
28185
28186 /*
28187 * Licence: LGPL
28188 */
28189
28190 /**
28191  * @class Roo.bootstrap.DocumentManager
28192  * @extends Roo.bootstrap.Component
28193  * Bootstrap DocumentManager class
28194  * @cfg {String} paramName default 'imageUpload'
28195  * @cfg {String} toolTipName default 'filename'
28196  * @cfg {String} method default POST
28197  * @cfg {String} url action url
28198  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28199  * @cfg {Boolean} multiple multiple upload default true
28200  * @cfg {Number} thumbSize default 300
28201  * @cfg {String} fieldLabel
28202  * @cfg {Number} labelWidth default 4
28203  * @cfg {String} labelAlign (left|top) default left
28204  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28205 * @cfg {Number} labellg set the width of label (1-12)
28206  * @cfg {Number} labelmd set the width of label (1-12)
28207  * @cfg {Number} labelsm set the width of label (1-12)
28208  * @cfg {Number} labelxs set the width of label (1-12)
28209  * 
28210  * @constructor
28211  * Create a new DocumentManager
28212  * @param {Object} config The config object
28213  */
28214
28215 Roo.bootstrap.DocumentManager = function(config){
28216     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28217     
28218     this.files = [];
28219     this.delegates = [];
28220     
28221     this.addEvents({
28222         /**
28223          * @event initial
28224          * Fire when initial the DocumentManager
28225          * @param {Roo.bootstrap.DocumentManager} this
28226          */
28227         "initial" : true,
28228         /**
28229          * @event inspect
28230          * inspect selected file
28231          * @param {Roo.bootstrap.DocumentManager} this
28232          * @param {File} file
28233          */
28234         "inspect" : true,
28235         /**
28236          * @event exception
28237          * Fire when xhr load exception
28238          * @param {Roo.bootstrap.DocumentManager} this
28239          * @param {XMLHttpRequest} xhr
28240          */
28241         "exception" : true,
28242         /**
28243          * @event afterupload
28244          * Fire when xhr load exception
28245          * @param {Roo.bootstrap.DocumentManager} this
28246          * @param {XMLHttpRequest} xhr
28247          */
28248         "afterupload" : true,
28249         /**
28250          * @event prepare
28251          * prepare the form data
28252          * @param {Roo.bootstrap.DocumentManager} this
28253          * @param {Object} formData
28254          */
28255         "prepare" : true,
28256         /**
28257          * @event remove
28258          * Fire when remove the file
28259          * @param {Roo.bootstrap.DocumentManager} this
28260          * @param {Object} file
28261          */
28262         "remove" : true,
28263         /**
28264          * @event refresh
28265          * Fire after refresh the file
28266          * @param {Roo.bootstrap.DocumentManager} this
28267          */
28268         "refresh" : true,
28269         /**
28270          * @event click
28271          * Fire after click the image
28272          * @param {Roo.bootstrap.DocumentManager} this
28273          * @param {Object} file
28274          */
28275         "click" : true,
28276         /**
28277          * @event edit
28278          * Fire when upload a image and editable set to true
28279          * @param {Roo.bootstrap.DocumentManager} this
28280          * @param {Object} file
28281          */
28282         "edit" : true,
28283         /**
28284          * @event beforeselectfile
28285          * Fire before select file
28286          * @param {Roo.bootstrap.DocumentManager} this
28287          */
28288         "beforeselectfile" : true,
28289         /**
28290          * @event process
28291          * Fire before process file
28292          * @param {Roo.bootstrap.DocumentManager} this
28293          * @param {Object} file
28294          */
28295         "process" : true
28296         
28297     });
28298 };
28299
28300 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28301     
28302     boxes : 0,
28303     inputName : '',
28304     thumbSize : 300,
28305     multiple : true,
28306     files : false,
28307     method : 'POST',
28308     url : '',
28309     paramName : 'imageUpload',
28310     toolTipName : 'filename',
28311     fieldLabel : '',
28312     labelWidth : 4,
28313     labelAlign : 'left',
28314     editable : true,
28315     delegates : false,
28316     xhr : false, 
28317     
28318     labellg : 0,
28319     labelmd : 0,
28320     labelsm : 0,
28321     labelxs : 0,
28322     
28323     getAutoCreate : function()
28324     {   
28325         var managerWidget = {
28326             tag : 'div',
28327             cls : 'roo-document-manager',
28328             cn : [
28329                 {
28330                     tag : 'input',
28331                     cls : 'roo-document-manager-selector',
28332                     type : 'file'
28333                 },
28334                 {
28335                     tag : 'div',
28336                     cls : 'roo-document-manager-uploader',
28337                     cn : [
28338                         {
28339                             tag : 'div',
28340                             cls : 'roo-document-manager-upload-btn',
28341                             html : '<i class="fa fa-plus"></i>'
28342                         }
28343                     ]
28344                     
28345                 }
28346             ]
28347         };
28348         
28349         var content = [
28350             {
28351                 tag : 'div',
28352                 cls : 'column col-md-12',
28353                 cn : managerWidget
28354             }
28355         ];
28356         
28357         if(this.fieldLabel.length){
28358             
28359             content = [
28360                 {
28361                     tag : 'div',
28362                     cls : 'column col-md-12',
28363                     html : this.fieldLabel
28364                 },
28365                 {
28366                     tag : 'div',
28367                     cls : 'column col-md-12',
28368                     cn : managerWidget
28369                 }
28370             ];
28371
28372             if(this.labelAlign == 'left'){
28373                 content = [
28374                     {
28375                         tag : 'div',
28376                         cls : 'column',
28377                         html : this.fieldLabel
28378                     },
28379                     {
28380                         tag : 'div',
28381                         cls : 'column',
28382                         cn : managerWidget
28383                     }
28384                 ];
28385                 
28386                 if(this.labelWidth > 12){
28387                     content[0].style = "width: " + this.labelWidth + 'px';
28388                 }
28389
28390                 if(this.labelWidth < 13 && this.labelmd == 0){
28391                     this.labelmd = this.labelWidth;
28392                 }
28393
28394                 if(this.labellg > 0){
28395                     content[0].cls += ' col-lg-' + this.labellg;
28396                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28397                 }
28398
28399                 if(this.labelmd > 0){
28400                     content[0].cls += ' col-md-' + this.labelmd;
28401                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28402                 }
28403
28404                 if(this.labelsm > 0){
28405                     content[0].cls += ' col-sm-' + this.labelsm;
28406                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28407                 }
28408
28409                 if(this.labelxs > 0){
28410                     content[0].cls += ' col-xs-' + this.labelxs;
28411                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28412                 }
28413                 
28414             }
28415         }
28416         
28417         var cfg = {
28418             tag : 'div',
28419             cls : 'row clearfix',
28420             cn : content
28421         };
28422         
28423         return cfg;
28424         
28425     },
28426     
28427     initEvents : function()
28428     {
28429         this.managerEl = this.el.select('.roo-document-manager', true).first();
28430         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28431         
28432         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28433         this.selectorEl.hide();
28434         
28435         if(this.multiple){
28436             this.selectorEl.attr('multiple', 'multiple');
28437         }
28438         
28439         this.selectorEl.on('change', this.onFileSelected, this);
28440         
28441         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28442         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28443         
28444         this.uploader.on('click', this.onUploaderClick, this);
28445         
28446         this.renderProgressDialog();
28447         
28448         var _this = this;
28449         
28450         window.addEventListener("resize", function() { _this.refresh(); } );
28451         
28452         this.fireEvent('initial', this);
28453     },
28454     
28455     renderProgressDialog : function()
28456     {
28457         var _this = this;
28458         
28459         this.progressDialog = new Roo.bootstrap.Modal({
28460             cls : 'roo-document-manager-progress-dialog',
28461             allow_close : false,
28462             title : '',
28463             buttons : [
28464                 {
28465                     name  :'cancel',
28466                     weight : 'danger',
28467                     html : 'Cancel'
28468                 }
28469             ], 
28470             listeners : { 
28471                 btnclick : function() {
28472                     _this.uploadCancel();
28473                     this.hide();
28474                 }
28475             }
28476         });
28477          
28478         this.progressDialog.render(Roo.get(document.body));
28479          
28480         this.progress = new Roo.bootstrap.Progress({
28481             cls : 'roo-document-manager-progress',
28482             active : true,
28483             striped : true
28484         });
28485         
28486         this.progress.render(this.progressDialog.getChildContainer());
28487         
28488         this.progressBar = new Roo.bootstrap.ProgressBar({
28489             cls : 'roo-document-manager-progress-bar',
28490             aria_valuenow : 0,
28491             aria_valuemin : 0,
28492             aria_valuemax : 12,
28493             panel : 'success'
28494         });
28495         
28496         this.progressBar.render(this.progress.getChildContainer());
28497     },
28498     
28499     onUploaderClick : function(e)
28500     {
28501         e.preventDefault();
28502      
28503         if(this.fireEvent('beforeselectfile', this) != false){
28504             this.selectorEl.dom.click();
28505         }
28506         
28507     },
28508     
28509     onFileSelected : function(e)
28510     {
28511         e.preventDefault();
28512         
28513         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28514             return;
28515         }
28516         
28517         Roo.each(this.selectorEl.dom.files, function(file){
28518             if(this.fireEvent('inspect', this, file) != false){
28519                 this.files.push(file);
28520             }
28521         }, this);
28522         
28523         this.queue();
28524         
28525     },
28526     
28527     queue : function()
28528     {
28529         this.selectorEl.dom.value = '';
28530         
28531         if(!this.files.length){
28532             return;
28533         }
28534         
28535         if(this.boxes > 0 && this.files.length > this.boxes){
28536             this.files = this.files.slice(0, this.boxes);
28537         }
28538         
28539         this.uploader.show();
28540         
28541         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28542             this.uploader.hide();
28543         }
28544         
28545         var _this = this;
28546         
28547         var files = [];
28548         
28549         var docs = [];
28550         
28551         Roo.each(this.files, function(file){
28552             
28553             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28554                 var f = this.renderPreview(file);
28555                 files.push(f);
28556                 return;
28557             }
28558             
28559             if(file.type.indexOf('image') != -1){
28560                 this.delegates.push(
28561                     (function(){
28562                         _this.process(file);
28563                     }).createDelegate(this)
28564                 );
28565         
28566                 return;
28567             }
28568             
28569             docs.push(
28570                 (function(){
28571                     _this.process(file);
28572                 }).createDelegate(this)
28573             );
28574             
28575         }, this);
28576         
28577         this.files = files;
28578         
28579         this.delegates = this.delegates.concat(docs);
28580         
28581         if(!this.delegates.length){
28582             this.refresh();
28583             return;
28584         }
28585         
28586         this.progressBar.aria_valuemax = this.delegates.length;
28587         
28588         this.arrange();
28589         
28590         return;
28591     },
28592     
28593     arrange : function()
28594     {
28595         if(!this.delegates.length){
28596             this.progressDialog.hide();
28597             this.refresh();
28598             return;
28599         }
28600         
28601         var delegate = this.delegates.shift();
28602         
28603         this.progressDialog.show();
28604         
28605         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28606         
28607         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28608         
28609         delegate();
28610     },
28611     
28612     refresh : function()
28613     {
28614         this.uploader.show();
28615         
28616         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28617             this.uploader.hide();
28618         }
28619         
28620         Roo.isTouch ? this.closable(false) : this.closable(true);
28621         
28622         this.fireEvent('refresh', this);
28623     },
28624     
28625     onRemove : function(e, el, o)
28626     {
28627         e.preventDefault();
28628         
28629         this.fireEvent('remove', this, o);
28630         
28631     },
28632     
28633     remove : function(o)
28634     {
28635         var files = [];
28636         
28637         Roo.each(this.files, function(file){
28638             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28639                 files.push(file);
28640                 return;
28641             }
28642
28643             o.target.remove();
28644
28645         }, this);
28646         
28647         this.files = files;
28648         
28649         this.refresh();
28650     },
28651     
28652     clear : function()
28653     {
28654         Roo.each(this.files, function(file){
28655             if(!file.target){
28656                 return;
28657             }
28658             
28659             file.target.remove();
28660
28661         }, this);
28662         
28663         this.files = [];
28664         
28665         this.refresh();
28666     },
28667     
28668     onClick : function(e, el, o)
28669     {
28670         e.preventDefault();
28671         
28672         this.fireEvent('click', this, o);
28673         
28674     },
28675     
28676     closable : function(closable)
28677     {
28678         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28679             
28680             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28681             
28682             if(closable){
28683                 el.show();
28684                 return;
28685             }
28686             
28687             el.hide();
28688             
28689         }, this);
28690     },
28691     
28692     xhrOnLoad : function(xhr)
28693     {
28694         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28695             el.remove();
28696         }, this);
28697         
28698         if (xhr.readyState !== 4) {
28699             this.arrange();
28700             this.fireEvent('exception', this, xhr);
28701             return;
28702         }
28703
28704         var response = Roo.decode(xhr.responseText);
28705         
28706         if(!response.success){
28707             this.arrange();
28708             this.fireEvent('exception', this, xhr);
28709             return;
28710         }
28711         
28712         var file = this.renderPreview(response.data);
28713         
28714         this.files.push(file);
28715         
28716         this.arrange();
28717         
28718         this.fireEvent('afterupload', this, xhr);
28719         
28720     },
28721     
28722     xhrOnError : function(xhr)
28723     {
28724         Roo.log('xhr on error');
28725         
28726         var response = Roo.decode(xhr.responseText);
28727           
28728         Roo.log(response);
28729         
28730         this.arrange();
28731     },
28732     
28733     process : function(file)
28734     {
28735         if(this.fireEvent('process', this, file) !== false){
28736             if(this.editable && file.type.indexOf('image') != -1){
28737                 this.fireEvent('edit', this, file);
28738                 return;
28739             }
28740
28741             this.uploadStart(file, false);
28742
28743             return;
28744         }
28745         
28746     },
28747     
28748     uploadStart : function(file, crop)
28749     {
28750         this.xhr = new XMLHttpRequest();
28751         
28752         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28753             this.arrange();
28754             return;
28755         }
28756         
28757         file.xhr = this.xhr;
28758             
28759         this.managerEl.createChild({
28760             tag : 'div',
28761             cls : 'roo-document-manager-loading',
28762             cn : [
28763                 {
28764                     tag : 'div',
28765                     tooltip : file.name,
28766                     cls : 'roo-document-manager-thumb',
28767                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28768                 }
28769             ]
28770
28771         });
28772
28773         this.xhr.open(this.method, this.url, true);
28774         
28775         var headers = {
28776             "Accept": "application/json",
28777             "Cache-Control": "no-cache",
28778             "X-Requested-With": "XMLHttpRequest"
28779         };
28780         
28781         for (var headerName in headers) {
28782             var headerValue = headers[headerName];
28783             if (headerValue) {
28784                 this.xhr.setRequestHeader(headerName, headerValue);
28785             }
28786         }
28787         
28788         var _this = this;
28789         
28790         this.xhr.onload = function()
28791         {
28792             _this.xhrOnLoad(_this.xhr);
28793         }
28794         
28795         this.xhr.onerror = function()
28796         {
28797             _this.xhrOnError(_this.xhr);
28798         }
28799         
28800         var formData = new FormData();
28801
28802         formData.append('returnHTML', 'NO');
28803         
28804         if(crop){
28805             formData.append('crop', crop);
28806         }
28807         
28808         formData.append(this.paramName, file, file.name);
28809         
28810         var options = {
28811             file : file, 
28812             manually : false
28813         };
28814         
28815         if(this.fireEvent('prepare', this, formData, options) != false){
28816             
28817             if(options.manually){
28818                 return;
28819             }
28820             
28821             this.xhr.send(formData);
28822             return;
28823         };
28824         
28825         this.uploadCancel();
28826     },
28827     
28828     uploadCancel : function()
28829     {
28830         if (this.xhr) {
28831             this.xhr.abort();
28832         }
28833         
28834         this.delegates = [];
28835         
28836         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28837             el.remove();
28838         }, this);
28839         
28840         this.arrange();
28841     },
28842     
28843     renderPreview : function(file)
28844     {
28845         if(typeof(file.target) != 'undefined' && file.target){
28846             return file;
28847         }
28848         
28849         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28850         
28851         var previewEl = this.managerEl.createChild({
28852             tag : 'div',
28853             cls : 'roo-document-manager-preview',
28854             cn : [
28855                 {
28856                     tag : 'div',
28857                     tooltip : file[this.toolTipName],
28858                     cls : 'roo-document-manager-thumb',
28859                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28860                 },
28861                 {
28862                     tag : 'button',
28863                     cls : 'close',
28864                     html : '<i class="fa fa-times-circle"></i>'
28865                 }
28866             ]
28867         });
28868
28869         var close = previewEl.select('button.close', true).first();
28870
28871         close.on('click', this.onRemove, this, file);
28872
28873         file.target = previewEl;
28874
28875         var image = previewEl.select('img', true).first();
28876         
28877         var _this = this;
28878         
28879         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28880         
28881         image.on('click', this.onClick, this, file);
28882         
28883         return file;
28884         
28885     },
28886     
28887     onPreviewLoad : function(file, image)
28888     {
28889         if(typeof(file.target) == 'undefined' || !file.target){
28890             return;
28891         }
28892         
28893         var width = image.dom.naturalWidth || image.dom.width;
28894         var height = image.dom.naturalHeight || image.dom.height;
28895         
28896         if(width > height){
28897             file.target.addClass('wide');
28898             return;
28899         }
28900         
28901         file.target.addClass('tall');
28902         return;
28903         
28904     },
28905     
28906     uploadFromSource : function(file, crop)
28907     {
28908         this.xhr = new XMLHttpRequest();
28909         
28910         this.managerEl.createChild({
28911             tag : 'div',
28912             cls : 'roo-document-manager-loading',
28913             cn : [
28914                 {
28915                     tag : 'div',
28916                     tooltip : file.name,
28917                     cls : 'roo-document-manager-thumb',
28918                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28919                 }
28920             ]
28921
28922         });
28923
28924         this.xhr.open(this.method, this.url, true);
28925         
28926         var headers = {
28927             "Accept": "application/json",
28928             "Cache-Control": "no-cache",
28929             "X-Requested-With": "XMLHttpRequest"
28930         };
28931         
28932         for (var headerName in headers) {
28933             var headerValue = headers[headerName];
28934             if (headerValue) {
28935                 this.xhr.setRequestHeader(headerName, headerValue);
28936             }
28937         }
28938         
28939         var _this = this;
28940         
28941         this.xhr.onload = function()
28942         {
28943             _this.xhrOnLoad(_this.xhr);
28944         }
28945         
28946         this.xhr.onerror = function()
28947         {
28948             _this.xhrOnError(_this.xhr);
28949         }
28950         
28951         var formData = new FormData();
28952
28953         formData.append('returnHTML', 'NO');
28954         
28955         formData.append('crop', crop);
28956         
28957         if(typeof(file.filename) != 'undefined'){
28958             formData.append('filename', file.filename);
28959         }
28960         
28961         if(typeof(file.mimetype) != 'undefined'){
28962             formData.append('mimetype', file.mimetype);
28963         }
28964         
28965         Roo.log(formData);
28966         
28967         if(this.fireEvent('prepare', this, formData) != false){
28968             this.xhr.send(formData);
28969         };
28970     }
28971 });
28972
28973 /*
28974 * Licence: LGPL
28975 */
28976
28977 /**
28978  * @class Roo.bootstrap.DocumentViewer
28979  * @extends Roo.bootstrap.Component
28980  * Bootstrap DocumentViewer class
28981  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28982  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28983  * 
28984  * @constructor
28985  * Create a new DocumentViewer
28986  * @param {Object} config The config object
28987  */
28988
28989 Roo.bootstrap.DocumentViewer = function(config){
28990     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28991     
28992     this.addEvents({
28993         /**
28994          * @event initial
28995          * Fire after initEvent
28996          * @param {Roo.bootstrap.DocumentViewer} this
28997          */
28998         "initial" : true,
28999         /**
29000          * @event click
29001          * Fire after click
29002          * @param {Roo.bootstrap.DocumentViewer} this
29003          */
29004         "click" : true,
29005         /**
29006          * @event download
29007          * Fire after download button
29008          * @param {Roo.bootstrap.DocumentViewer} this
29009          */
29010         "download" : true,
29011         /**
29012          * @event trash
29013          * Fire after trash button
29014          * @param {Roo.bootstrap.DocumentViewer} this
29015          */
29016         "trash" : true
29017         
29018     });
29019 };
29020
29021 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29022     
29023     showDownload : true,
29024     
29025     showTrash : true,
29026     
29027     getAutoCreate : function()
29028     {
29029         var cfg = {
29030             tag : 'div',
29031             cls : 'roo-document-viewer',
29032             cn : [
29033                 {
29034                     tag : 'div',
29035                     cls : 'roo-document-viewer-body',
29036                     cn : [
29037                         {
29038                             tag : 'div',
29039                             cls : 'roo-document-viewer-thumb',
29040                             cn : [
29041                                 {
29042                                     tag : 'img',
29043                                     cls : 'roo-document-viewer-image'
29044                                 }
29045                             ]
29046                         }
29047                     ]
29048                 },
29049                 {
29050                     tag : 'div',
29051                     cls : 'roo-document-viewer-footer',
29052                     cn : {
29053                         tag : 'div',
29054                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29055                         cn : [
29056                             {
29057                                 tag : 'div',
29058                                 cls : 'btn-group roo-document-viewer-download',
29059                                 cn : [
29060                                     {
29061                                         tag : 'button',
29062                                         cls : 'btn btn-default',
29063                                         html : '<i class="fa fa-download"></i>'
29064                                     }
29065                                 ]
29066                             },
29067                             {
29068                                 tag : 'div',
29069                                 cls : 'btn-group roo-document-viewer-trash',
29070                                 cn : [
29071                                     {
29072                                         tag : 'button',
29073                                         cls : 'btn btn-default',
29074                                         html : '<i class="fa fa-trash"></i>'
29075                                     }
29076                                 ]
29077                             }
29078                         ]
29079                     }
29080                 }
29081             ]
29082         };
29083         
29084         return cfg;
29085     },
29086     
29087     initEvents : function()
29088     {
29089         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29090         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29091         
29092         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29093         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29094         
29095         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29096         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29097         
29098         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29099         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29100         
29101         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29102         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29103         
29104         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29105         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29106         
29107         this.bodyEl.on('click', this.onClick, this);
29108         this.downloadBtn.on('click', this.onDownload, this);
29109         this.trashBtn.on('click', this.onTrash, this);
29110         
29111         this.downloadBtn.hide();
29112         this.trashBtn.hide();
29113         
29114         if(this.showDownload){
29115             this.downloadBtn.show();
29116         }
29117         
29118         if(this.showTrash){
29119             this.trashBtn.show();
29120         }
29121         
29122         if(!this.showDownload && !this.showTrash) {
29123             this.footerEl.hide();
29124         }
29125         
29126     },
29127     
29128     initial : function()
29129     {
29130         this.fireEvent('initial', this);
29131         
29132     },
29133     
29134     onClick : function(e)
29135     {
29136         e.preventDefault();
29137         
29138         this.fireEvent('click', this);
29139     },
29140     
29141     onDownload : function(e)
29142     {
29143         e.preventDefault();
29144         
29145         this.fireEvent('download', this);
29146     },
29147     
29148     onTrash : function(e)
29149     {
29150         e.preventDefault();
29151         
29152         this.fireEvent('trash', this);
29153     }
29154     
29155 });
29156 /*
29157  * - LGPL
29158  *
29159  * nav progress bar
29160  * 
29161  */
29162
29163 /**
29164  * @class Roo.bootstrap.NavProgressBar
29165  * @extends Roo.bootstrap.Component
29166  * Bootstrap NavProgressBar class
29167  * 
29168  * @constructor
29169  * Create a new nav progress bar
29170  * @param {Object} config The config object
29171  */
29172
29173 Roo.bootstrap.NavProgressBar = function(config){
29174     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29175
29176     this.bullets = this.bullets || [];
29177    
29178 //    Roo.bootstrap.NavProgressBar.register(this);
29179      this.addEvents({
29180         /**
29181              * @event changed
29182              * Fires when the active item changes
29183              * @param {Roo.bootstrap.NavProgressBar} this
29184              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29185              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29186          */
29187         'changed': true
29188      });
29189     
29190 };
29191
29192 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29193     
29194     bullets : [],
29195     barItems : [],
29196     
29197     getAutoCreate : function()
29198     {
29199         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29200         
29201         cfg = {
29202             tag : 'div',
29203             cls : 'roo-navigation-bar-group',
29204             cn : [
29205                 {
29206                     tag : 'div',
29207                     cls : 'roo-navigation-top-bar'
29208                 },
29209                 {
29210                     tag : 'div',
29211                     cls : 'roo-navigation-bullets-bar',
29212                     cn : [
29213                         {
29214                             tag : 'ul',
29215                             cls : 'roo-navigation-bar'
29216                         }
29217                     ]
29218                 },
29219                 
29220                 {
29221                     tag : 'div',
29222                     cls : 'roo-navigation-bottom-bar'
29223                 }
29224             ]
29225             
29226         };
29227         
29228         return cfg;
29229         
29230     },
29231     
29232     initEvents: function() 
29233     {
29234         
29235     },
29236     
29237     onRender : function(ct, position) 
29238     {
29239         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29240         
29241         if(this.bullets.length){
29242             Roo.each(this.bullets, function(b){
29243                this.addItem(b);
29244             }, this);
29245         }
29246         
29247         this.format();
29248         
29249     },
29250     
29251     addItem : function(cfg)
29252     {
29253         var item = new Roo.bootstrap.NavProgressItem(cfg);
29254         
29255         item.parentId = this.id;
29256         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29257         
29258         if(cfg.html){
29259             var top = new Roo.bootstrap.Element({
29260                 tag : 'div',
29261                 cls : 'roo-navigation-bar-text'
29262             });
29263             
29264             var bottom = new Roo.bootstrap.Element({
29265                 tag : 'div',
29266                 cls : 'roo-navigation-bar-text'
29267             });
29268             
29269             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29270             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29271             
29272             var topText = new Roo.bootstrap.Element({
29273                 tag : 'span',
29274                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29275             });
29276             
29277             var bottomText = new Roo.bootstrap.Element({
29278                 tag : 'span',
29279                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29280             });
29281             
29282             topText.onRender(top.el, null);
29283             bottomText.onRender(bottom.el, null);
29284             
29285             item.topEl = top;
29286             item.bottomEl = bottom;
29287         }
29288         
29289         this.barItems.push(item);
29290         
29291         return item;
29292     },
29293     
29294     getActive : function()
29295     {
29296         var active = false;
29297         
29298         Roo.each(this.barItems, function(v){
29299             
29300             if (!v.isActive()) {
29301                 return;
29302             }
29303             
29304             active = v;
29305             return false;
29306             
29307         });
29308         
29309         return active;
29310     },
29311     
29312     setActiveItem : function(item)
29313     {
29314         var prev = false;
29315         
29316         Roo.each(this.barItems, function(v){
29317             if (v.rid == item.rid) {
29318                 return ;
29319             }
29320             
29321             if (v.isActive()) {
29322                 v.setActive(false);
29323                 prev = v;
29324             }
29325         });
29326
29327         item.setActive(true);
29328         
29329         this.fireEvent('changed', this, item, prev);
29330     },
29331     
29332     getBarItem: function(rid)
29333     {
29334         var ret = false;
29335         
29336         Roo.each(this.barItems, function(e) {
29337             if (e.rid != rid) {
29338                 return;
29339             }
29340             
29341             ret =  e;
29342             return false;
29343         });
29344         
29345         return ret;
29346     },
29347     
29348     indexOfItem : function(item)
29349     {
29350         var index = false;
29351         
29352         Roo.each(this.barItems, function(v, i){
29353             
29354             if (v.rid != item.rid) {
29355                 return;
29356             }
29357             
29358             index = i;
29359             return false
29360         });
29361         
29362         return index;
29363     },
29364     
29365     setActiveNext : function()
29366     {
29367         var i = this.indexOfItem(this.getActive());
29368         
29369         if (i > this.barItems.length) {
29370             return;
29371         }
29372         
29373         this.setActiveItem(this.barItems[i+1]);
29374     },
29375     
29376     setActivePrev : function()
29377     {
29378         var i = this.indexOfItem(this.getActive());
29379         
29380         if (i  < 1) {
29381             return;
29382         }
29383         
29384         this.setActiveItem(this.barItems[i-1]);
29385     },
29386     
29387     format : function()
29388     {
29389         if(!this.barItems.length){
29390             return;
29391         }
29392      
29393         var width = 100 / this.barItems.length;
29394         
29395         Roo.each(this.barItems, function(i){
29396             i.el.setStyle('width', width + '%');
29397             i.topEl.el.setStyle('width', width + '%');
29398             i.bottomEl.el.setStyle('width', width + '%');
29399         }, this);
29400         
29401     }
29402     
29403 });
29404 /*
29405  * - LGPL
29406  *
29407  * Nav Progress Item
29408  * 
29409  */
29410
29411 /**
29412  * @class Roo.bootstrap.NavProgressItem
29413  * @extends Roo.bootstrap.Component
29414  * Bootstrap NavProgressItem class
29415  * @cfg {String} rid the reference id
29416  * @cfg {Boolean} active (true|false) Is item active default false
29417  * @cfg {Boolean} disabled (true|false) Is item active default false
29418  * @cfg {String} html
29419  * @cfg {String} position (top|bottom) text position default bottom
29420  * @cfg {String} icon show icon instead of number
29421  * 
29422  * @constructor
29423  * Create a new NavProgressItem
29424  * @param {Object} config The config object
29425  */
29426 Roo.bootstrap.NavProgressItem = function(config){
29427     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29428     this.addEvents({
29429         // raw events
29430         /**
29431          * @event click
29432          * The raw click event for the entire grid.
29433          * @param {Roo.bootstrap.NavProgressItem} this
29434          * @param {Roo.EventObject} e
29435          */
29436         "click" : true
29437     });
29438    
29439 };
29440
29441 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29442     
29443     rid : '',
29444     active : false,
29445     disabled : false,
29446     html : '',
29447     position : 'bottom',
29448     icon : false,
29449     
29450     getAutoCreate : function()
29451     {
29452         var iconCls = 'roo-navigation-bar-item-icon';
29453         
29454         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29455         
29456         var cfg = {
29457             tag: 'li',
29458             cls: 'roo-navigation-bar-item',
29459             cn : [
29460                 {
29461                     tag : 'i',
29462                     cls : iconCls
29463                 }
29464             ]
29465         };
29466         
29467         if(this.active){
29468             cfg.cls += ' active';
29469         }
29470         if(this.disabled){
29471             cfg.cls += ' disabled';
29472         }
29473         
29474         return cfg;
29475     },
29476     
29477     disable : function()
29478     {
29479         this.setDisabled(true);
29480     },
29481     
29482     enable : function()
29483     {
29484         this.setDisabled(false);
29485     },
29486     
29487     initEvents: function() 
29488     {
29489         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29490         
29491         this.iconEl.on('click', this.onClick, this);
29492     },
29493     
29494     onClick : function(e)
29495     {
29496         e.preventDefault();
29497         
29498         if(this.disabled){
29499             return;
29500         }
29501         
29502         if(this.fireEvent('click', this, e) === false){
29503             return;
29504         };
29505         
29506         this.parent().setActiveItem(this);
29507     },
29508     
29509     isActive: function () 
29510     {
29511         return this.active;
29512     },
29513     
29514     setActive : function(state)
29515     {
29516         if(this.active == state){
29517             return;
29518         }
29519         
29520         this.active = state;
29521         
29522         if (state) {
29523             this.el.addClass('active');
29524             return;
29525         }
29526         
29527         this.el.removeClass('active');
29528         
29529         return;
29530     },
29531     
29532     setDisabled : function(state)
29533     {
29534         if(this.disabled == state){
29535             return;
29536         }
29537         
29538         this.disabled = state;
29539         
29540         if (state) {
29541             this.el.addClass('disabled');
29542             return;
29543         }
29544         
29545         this.el.removeClass('disabled');
29546     },
29547     
29548     tooltipEl : function()
29549     {
29550         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29551     }
29552 });
29553  
29554
29555  /*
29556  * - LGPL
29557  *
29558  * FieldLabel
29559  * 
29560  */
29561
29562 /**
29563  * @class Roo.bootstrap.FieldLabel
29564  * @extends Roo.bootstrap.Component
29565  * Bootstrap FieldLabel class
29566  * @cfg {String} html contents of the element
29567  * @cfg {String} tag tag of the element default label
29568  * @cfg {String} cls class of the element
29569  * @cfg {String} target label target 
29570  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29571  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29572  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29573  * @cfg {String} iconTooltip default "This field is required"
29574  * 
29575  * @constructor
29576  * Create a new FieldLabel
29577  * @param {Object} config The config object
29578  */
29579
29580 Roo.bootstrap.FieldLabel = function(config){
29581     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29582     
29583     this.addEvents({
29584             /**
29585              * @event invalid
29586              * Fires after the field has been marked as invalid.
29587              * @param {Roo.form.FieldLabel} this
29588              * @param {String} msg The validation message
29589              */
29590             invalid : true,
29591             /**
29592              * @event valid
29593              * Fires after the field has been validated with no errors.
29594              * @param {Roo.form.FieldLabel} this
29595              */
29596             valid : true
29597         });
29598 };
29599
29600 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29601     
29602     tag: 'label',
29603     cls: '',
29604     html: '',
29605     target: '',
29606     allowBlank : true,
29607     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29608     validClass : 'text-success fa fa-lg fa-check',
29609     iconTooltip : 'This field is required',
29610     
29611     getAutoCreate : function(){
29612         
29613         var cfg = {
29614             tag : this.tag,
29615             cls : 'roo-bootstrap-field-label ' + this.cls,
29616             for : this.target,
29617             cn : [
29618                 {
29619                     tag : 'i',
29620                     cls : '',
29621                     tooltip : this.iconTooltip
29622                 },
29623                 {
29624                     tag : 'span',
29625                     html : this.html
29626                 }
29627             ] 
29628         };
29629         
29630         return cfg;
29631     },
29632     
29633     initEvents: function() 
29634     {
29635         Roo.bootstrap.Element.superclass.initEvents.call(this);
29636         
29637         this.iconEl = this.el.select('i', true).first();
29638         
29639         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29640         
29641         Roo.bootstrap.FieldLabel.register(this);
29642     },
29643     
29644     /**
29645      * Mark this field as valid
29646      */
29647     markValid : function()
29648     {
29649         this.iconEl.show();
29650         
29651         this.iconEl.removeClass(this.invalidClass);
29652         
29653         this.iconEl.addClass(this.validClass);
29654         
29655         this.fireEvent('valid', this);
29656     },
29657     
29658     /**
29659      * Mark this field as invalid
29660      * @param {String} msg The validation message
29661      */
29662     markInvalid : function(msg)
29663     {
29664         this.iconEl.show();
29665         
29666         this.iconEl.removeClass(this.validClass);
29667         
29668         this.iconEl.addClass(this.invalidClass);
29669         
29670         this.fireEvent('invalid', this, msg);
29671     }
29672     
29673    
29674 });
29675
29676 Roo.apply(Roo.bootstrap.FieldLabel, {
29677     
29678     groups: {},
29679     
29680      /**
29681     * register a FieldLabel Group
29682     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29683     */
29684     register : function(label)
29685     {
29686         if(this.groups.hasOwnProperty(label.target)){
29687             return;
29688         }
29689      
29690         this.groups[label.target] = label;
29691         
29692     },
29693     /**
29694     * fetch a FieldLabel Group based on the target
29695     * @param {string} target
29696     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29697     */
29698     get: function(target) {
29699         if (typeof(this.groups[target]) == 'undefined') {
29700             return false;
29701         }
29702         
29703         return this.groups[target] ;
29704     }
29705 });
29706
29707  
29708
29709  /*
29710  * - LGPL
29711  *
29712  * page DateSplitField.
29713  * 
29714  */
29715
29716
29717 /**
29718  * @class Roo.bootstrap.DateSplitField
29719  * @extends Roo.bootstrap.Component
29720  * Bootstrap DateSplitField class
29721  * @cfg {string} fieldLabel - the label associated
29722  * @cfg {Number} labelWidth set the width of label (0-12)
29723  * @cfg {String} labelAlign (top|left)
29724  * @cfg {Boolean} dayAllowBlank (true|false) default false
29725  * @cfg {Boolean} monthAllowBlank (true|false) default false
29726  * @cfg {Boolean} yearAllowBlank (true|false) default false
29727  * @cfg {string} dayPlaceholder 
29728  * @cfg {string} monthPlaceholder
29729  * @cfg {string} yearPlaceholder
29730  * @cfg {string} dayFormat default 'd'
29731  * @cfg {string} monthFormat default 'm'
29732  * @cfg {string} yearFormat default 'Y'
29733  * @cfg {Number} labellg set the width of label (1-12)
29734  * @cfg {Number} labelmd set the width of label (1-12)
29735  * @cfg {Number} labelsm set the width of label (1-12)
29736  * @cfg {Number} labelxs set the width of label (1-12)
29737
29738  *     
29739  * @constructor
29740  * Create a new DateSplitField
29741  * @param {Object} config The config object
29742  */
29743
29744 Roo.bootstrap.DateSplitField = function(config){
29745     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29746     
29747     this.addEvents({
29748         // raw events
29749          /**
29750          * @event years
29751          * getting the data of years
29752          * @param {Roo.bootstrap.DateSplitField} this
29753          * @param {Object} years
29754          */
29755         "years" : true,
29756         /**
29757          * @event days
29758          * getting the data of days
29759          * @param {Roo.bootstrap.DateSplitField} this
29760          * @param {Object} days
29761          */
29762         "days" : true,
29763         /**
29764          * @event invalid
29765          * Fires after the field has been marked as invalid.
29766          * @param {Roo.form.Field} this
29767          * @param {String} msg The validation message
29768          */
29769         invalid : true,
29770        /**
29771          * @event valid
29772          * Fires after the field has been validated with no errors.
29773          * @param {Roo.form.Field} this
29774          */
29775         valid : true
29776     });
29777 };
29778
29779 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29780     
29781     fieldLabel : '',
29782     labelAlign : 'top',
29783     labelWidth : 3,
29784     dayAllowBlank : false,
29785     monthAllowBlank : false,
29786     yearAllowBlank : false,
29787     dayPlaceholder : '',
29788     monthPlaceholder : '',
29789     yearPlaceholder : '',
29790     dayFormat : 'd',
29791     monthFormat : 'm',
29792     yearFormat : 'Y',
29793     isFormField : true,
29794     labellg : 0,
29795     labelmd : 0,
29796     labelsm : 0,
29797     labelxs : 0,
29798     
29799     getAutoCreate : function()
29800     {
29801         var cfg = {
29802             tag : 'div',
29803             cls : 'row roo-date-split-field-group',
29804             cn : [
29805                 {
29806                     tag : 'input',
29807                     type : 'hidden',
29808                     cls : 'form-hidden-field roo-date-split-field-group-value',
29809                     name : this.name
29810                 }
29811             ]
29812         };
29813         
29814         var labelCls = 'col-md-12';
29815         var contentCls = 'col-md-4';
29816         
29817         if(this.fieldLabel){
29818             
29819             var label = {
29820                 tag : 'div',
29821                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29822                 cn : [
29823                     {
29824                         tag : 'label',
29825                         html : this.fieldLabel
29826                     }
29827                 ]
29828             };
29829             
29830             if(this.labelAlign == 'left'){
29831             
29832                 if(this.labelWidth > 12){
29833                     label.style = "width: " + this.labelWidth + 'px';
29834                 }
29835
29836                 if(this.labelWidth < 13 && this.labelmd == 0){
29837                     this.labelmd = this.labelWidth;
29838                 }
29839
29840                 if(this.labellg > 0){
29841                     labelCls = ' col-lg-' + this.labellg;
29842                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29843                 }
29844
29845                 if(this.labelmd > 0){
29846                     labelCls = ' col-md-' + this.labelmd;
29847                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29848                 }
29849
29850                 if(this.labelsm > 0){
29851                     labelCls = ' col-sm-' + this.labelsm;
29852                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29853                 }
29854
29855                 if(this.labelxs > 0){
29856                     labelCls = ' col-xs-' + this.labelxs;
29857                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29858                 }
29859             }
29860             
29861             label.cls += ' ' + labelCls;
29862             
29863             cfg.cn.push(label);
29864         }
29865         
29866         Roo.each(['day', 'month', 'year'], function(t){
29867             cfg.cn.push({
29868                 tag : 'div',
29869                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29870             });
29871         }, this);
29872         
29873         return cfg;
29874     },
29875     
29876     inputEl: function ()
29877     {
29878         return this.el.select('.roo-date-split-field-group-value', true).first();
29879     },
29880     
29881     onRender : function(ct, position) 
29882     {
29883         var _this = this;
29884         
29885         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29886         
29887         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29888         
29889         this.dayField = new Roo.bootstrap.ComboBox({
29890             allowBlank : this.dayAllowBlank,
29891             alwaysQuery : true,
29892             displayField : 'value',
29893             editable : false,
29894             fieldLabel : '',
29895             forceSelection : true,
29896             mode : 'local',
29897             placeholder : this.dayPlaceholder,
29898             selectOnFocus : true,
29899             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29900             triggerAction : 'all',
29901             typeAhead : true,
29902             valueField : 'value',
29903             store : new Roo.data.SimpleStore({
29904                 data : (function() {    
29905                     var days = [];
29906                     _this.fireEvent('days', _this, days);
29907                     return days;
29908                 })(),
29909                 fields : [ 'value' ]
29910             }),
29911             listeners : {
29912                 select : function (_self, record, index)
29913                 {
29914                     _this.setValue(_this.getValue());
29915                 }
29916             }
29917         });
29918
29919         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29920         
29921         this.monthField = new Roo.bootstrap.MonthField({
29922             after : '<i class=\"fa fa-calendar\"></i>',
29923             allowBlank : this.monthAllowBlank,
29924             placeholder : this.monthPlaceholder,
29925             readOnly : true,
29926             listeners : {
29927                 render : function (_self)
29928                 {
29929                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29930                         e.preventDefault();
29931                         _self.focus();
29932                     });
29933                 },
29934                 select : function (_self, oldvalue, newvalue)
29935                 {
29936                     _this.setValue(_this.getValue());
29937                 }
29938             }
29939         });
29940         
29941         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29942         
29943         this.yearField = new Roo.bootstrap.ComboBox({
29944             allowBlank : this.yearAllowBlank,
29945             alwaysQuery : true,
29946             displayField : 'value',
29947             editable : false,
29948             fieldLabel : '',
29949             forceSelection : true,
29950             mode : 'local',
29951             placeholder : this.yearPlaceholder,
29952             selectOnFocus : true,
29953             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29954             triggerAction : 'all',
29955             typeAhead : true,
29956             valueField : 'value',
29957             store : new Roo.data.SimpleStore({
29958                 data : (function() {
29959                     var years = [];
29960                     _this.fireEvent('years', _this, years);
29961                     return years;
29962                 })(),
29963                 fields : [ 'value' ]
29964             }),
29965             listeners : {
29966                 select : function (_self, record, index)
29967                 {
29968                     _this.setValue(_this.getValue());
29969                 }
29970             }
29971         });
29972
29973         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29974     },
29975     
29976     setValue : function(v, format)
29977     {
29978         this.inputEl.dom.value = v;
29979         
29980         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29981         
29982         var d = Date.parseDate(v, f);
29983         
29984         if(!d){
29985             this.validate();
29986             return;
29987         }
29988         
29989         this.setDay(d.format(this.dayFormat));
29990         this.setMonth(d.format(this.monthFormat));
29991         this.setYear(d.format(this.yearFormat));
29992         
29993         this.validate();
29994         
29995         return;
29996     },
29997     
29998     setDay : function(v)
29999     {
30000         this.dayField.setValue(v);
30001         this.inputEl.dom.value = this.getValue();
30002         this.validate();
30003         return;
30004     },
30005     
30006     setMonth : function(v)
30007     {
30008         this.monthField.setValue(v, true);
30009         this.inputEl.dom.value = this.getValue();
30010         this.validate();
30011         return;
30012     },
30013     
30014     setYear : function(v)
30015     {
30016         this.yearField.setValue(v);
30017         this.inputEl.dom.value = this.getValue();
30018         this.validate();
30019         return;
30020     },
30021     
30022     getDay : function()
30023     {
30024         return this.dayField.getValue();
30025     },
30026     
30027     getMonth : function()
30028     {
30029         return this.monthField.getValue();
30030     },
30031     
30032     getYear : function()
30033     {
30034         return this.yearField.getValue();
30035     },
30036     
30037     getValue : function()
30038     {
30039         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30040         
30041         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30042         
30043         return date;
30044     },
30045     
30046     reset : function()
30047     {
30048         this.setDay('');
30049         this.setMonth('');
30050         this.setYear('');
30051         this.inputEl.dom.value = '';
30052         this.validate();
30053         return;
30054     },
30055     
30056     validate : function()
30057     {
30058         var d = this.dayField.validate();
30059         var m = this.monthField.validate();
30060         var y = this.yearField.validate();
30061         
30062         var valid = true;
30063         
30064         if(
30065                 (!this.dayAllowBlank && !d) ||
30066                 (!this.monthAllowBlank && !m) ||
30067                 (!this.yearAllowBlank && !y)
30068         ){
30069             valid = false;
30070         }
30071         
30072         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30073             return valid;
30074         }
30075         
30076         if(valid){
30077             this.markValid();
30078             return valid;
30079         }
30080         
30081         this.markInvalid();
30082         
30083         return valid;
30084     },
30085     
30086     markValid : function()
30087     {
30088         
30089         var label = this.el.select('label', true).first();
30090         var icon = this.el.select('i.fa-star', true).first();
30091
30092         if(label && icon){
30093             icon.remove();
30094         }
30095         
30096         this.fireEvent('valid', this);
30097     },
30098     
30099      /**
30100      * Mark this field as invalid
30101      * @param {String} msg The validation message
30102      */
30103     markInvalid : function(msg)
30104     {
30105         
30106         var label = this.el.select('label', true).first();
30107         var icon = this.el.select('i.fa-star', true).first();
30108
30109         if(label && !icon){
30110             this.el.select('.roo-date-split-field-label', true).createChild({
30111                 tag : 'i',
30112                 cls : 'text-danger fa fa-lg fa-star',
30113                 tooltip : 'This field is required',
30114                 style : 'margin-right:5px;'
30115             }, label, true);
30116         }
30117         
30118         this.fireEvent('invalid', this, msg);
30119     },
30120     
30121     clearInvalid : function()
30122     {
30123         var label = this.el.select('label', true).first();
30124         var icon = this.el.select('i.fa-star', true).first();
30125
30126         if(label && icon){
30127             icon.remove();
30128         }
30129         
30130         this.fireEvent('valid', this);
30131     },
30132     
30133     getName: function()
30134     {
30135         return this.name;
30136     }
30137     
30138 });
30139
30140  /**
30141  *
30142  * This is based on 
30143  * http://masonry.desandro.com
30144  *
30145  * The idea is to render all the bricks based on vertical width...
30146  *
30147  * The original code extends 'outlayer' - we might need to use that....
30148  * 
30149  */
30150
30151
30152 /**
30153  * @class Roo.bootstrap.LayoutMasonry
30154  * @extends Roo.bootstrap.Component
30155  * Bootstrap Layout Masonry class
30156  * 
30157  * @constructor
30158  * Create a new Element
30159  * @param {Object} config The config object
30160  */
30161
30162 Roo.bootstrap.LayoutMasonry = function(config){
30163     
30164     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30165     
30166     this.bricks = [];
30167     
30168     Roo.bootstrap.LayoutMasonry.register(this);
30169     
30170     this.addEvents({
30171         // raw events
30172         /**
30173          * @event layout
30174          * Fire after layout the items
30175          * @param {Roo.bootstrap.LayoutMasonry} this
30176          * @param {Roo.EventObject} e
30177          */
30178         "layout" : true
30179     });
30180     
30181 };
30182
30183 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30184     
30185     /**
30186      * @cfg {Boolean} isLayoutInstant = no animation?
30187      */   
30188     isLayoutInstant : false, // needed?
30189    
30190     /**
30191      * @cfg {Number} boxWidth  width of the columns
30192      */   
30193     boxWidth : 450,
30194     
30195       /**
30196      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30197      */   
30198     boxHeight : 0,
30199     
30200     /**
30201      * @cfg {Number} padWidth padding below box..
30202      */   
30203     padWidth : 10, 
30204     
30205     /**
30206      * @cfg {Number} gutter gutter width..
30207      */   
30208     gutter : 10,
30209     
30210      /**
30211      * @cfg {Number} maxCols maximum number of columns
30212      */   
30213     
30214     maxCols: 0,
30215     
30216     /**
30217      * @cfg {Boolean} isAutoInitial defalut true
30218      */   
30219     isAutoInitial : true, 
30220     
30221     containerWidth: 0,
30222     
30223     /**
30224      * @cfg {Boolean} isHorizontal defalut false
30225      */   
30226     isHorizontal : false, 
30227
30228     currentSize : null,
30229     
30230     tag: 'div',
30231     
30232     cls: '',
30233     
30234     bricks: null, //CompositeElement
30235     
30236     cols : 1,
30237     
30238     _isLayoutInited : false,
30239     
30240 //    isAlternative : false, // only use for vertical layout...
30241     
30242     /**
30243      * @cfg {Number} alternativePadWidth padding below box..
30244      */   
30245     alternativePadWidth : 50,
30246     
30247     selectedBrick : [],
30248     
30249     getAutoCreate : function(){
30250         
30251         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30252         
30253         var cfg = {
30254             tag: this.tag,
30255             cls: 'blog-masonary-wrapper ' + this.cls,
30256             cn : {
30257                 cls : 'mas-boxes masonary'
30258             }
30259         };
30260         
30261         return cfg;
30262     },
30263     
30264     getChildContainer: function( )
30265     {
30266         if (this.boxesEl) {
30267             return this.boxesEl;
30268         }
30269         
30270         this.boxesEl = this.el.select('.mas-boxes').first();
30271         
30272         return this.boxesEl;
30273     },
30274     
30275     
30276     initEvents : function()
30277     {
30278         var _this = this;
30279         
30280         if(this.isAutoInitial){
30281             Roo.log('hook children rendered');
30282             this.on('childrenrendered', function() {
30283                 Roo.log('children rendered');
30284                 _this.initial();
30285             } ,this);
30286         }
30287     },
30288     
30289     initial : function()
30290     {
30291         this.selectedBrick = [];
30292         
30293         this.currentSize = this.el.getBox(true);
30294         
30295         Roo.EventManager.onWindowResize(this.resize, this); 
30296
30297         if(!this.isAutoInitial){
30298             this.layout();
30299             return;
30300         }
30301         
30302         this.layout();
30303         
30304         return;
30305         //this.layout.defer(500,this);
30306         
30307     },
30308     
30309     resize : function()
30310     {
30311         var cs = this.el.getBox(true);
30312         
30313         if (
30314                 this.currentSize.width == cs.width && 
30315                 this.currentSize.x == cs.x && 
30316                 this.currentSize.height == cs.height && 
30317                 this.currentSize.y == cs.y 
30318         ) {
30319             Roo.log("no change in with or X or Y");
30320             return;
30321         }
30322         
30323         this.currentSize = cs;
30324         
30325         this.layout();
30326         
30327     },
30328     
30329     layout : function()
30330     {   
30331         this._resetLayout();
30332         
30333         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30334         
30335         this.layoutItems( isInstant );
30336       
30337         this._isLayoutInited = true;
30338         
30339         this.fireEvent('layout', this);
30340         
30341     },
30342     
30343     _resetLayout : function()
30344     {
30345         if(this.isHorizontal){
30346             this.horizontalMeasureColumns();
30347             return;
30348         }
30349         
30350         this.verticalMeasureColumns();
30351         
30352     },
30353     
30354     verticalMeasureColumns : function()
30355     {
30356         this.getContainerWidth();
30357         
30358 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30359 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30360 //            return;
30361 //        }
30362         
30363         var boxWidth = this.boxWidth + this.padWidth;
30364         
30365         if(this.containerWidth < this.boxWidth){
30366             boxWidth = this.containerWidth
30367         }
30368         
30369         var containerWidth = this.containerWidth;
30370         
30371         var cols = Math.floor(containerWidth / boxWidth);
30372         
30373         this.cols = Math.max( cols, 1 );
30374         
30375         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30376         
30377         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30378         
30379         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30380         
30381         this.colWidth = boxWidth + avail - this.padWidth;
30382         
30383         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30384         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30385     },
30386     
30387     horizontalMeasureColumns : function()
30388     {
30389         this.getContainerWidth();
30390         
30391         var boxWidth = this.boxWidth;
30392         
30393         if(this.containerWidth < boxWidth){
30394             boxWidth = this.containerWidth;
30395         }
30396         
30397         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30398         
30399         this.el.setHeight(boxWidth);
30400         
30401     },
30402     
30403     getContainerWidth : function()
30404     {
30405         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30406     },
30407     
30408     layoutItems : function( isInstant )
30409     {
30410         Roo.log(this.bricks);
30411         
30412         var items = Roo.apply([], this.bricks);
30413         
30414         if(this.isHorizontal){
30415             this._horizontalLayoutItems( items , isInstant );
30416             return;
30417         }
30418         
30419 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30420 //            this._verticalAlternativeLayoutItems( items , isInstant );
30421 //            return;
30422 //        }
30423         
30424         this._verticalLayoutItems( items , isInstant );
30425         
30426     },
30427     
30428     _verticalLayoutItems : function ( items , isInstant)
30429     {
30430         if ( !items || !items.length ) {
30431             return;
30432         }
30433         
30434         var standard = [
30435             ['xs', 'xs', 'xs', 'tall'],
30436             ['xs', 'xs', 'tall'],
30437             ['xs', 'xs', 'sm'],
30438             ['xs', 'xs', 'xs'],
30439             ['xs', 'tall'],
30440             ['xs', 'sm'],
30441             ['xs', 'xs'],
30442             ['xs'],
30443             
30444             ['sm', 'xs', 'xs'],
30445             ['sm', 'xs'],
30446             ['sm'],
30447             
30448             ['tall', 'xs', 'xs', 'xs'],
30449             ['tall', 'xs', 'xs'],
30450             ['tall', 'xs'],
30451             ['tall']
30452             
30453         ];
30454         
30455         var queue = [];
30456         
30457         var boxes = [];
30458         
30459         var box = [];
30460         
30461         Roo.each(items, function(item, k){
30462             
30463             switch (item.size) {
30464                 // these layouts take up a full box,
30465                 case 'md' :
30466                 case 'md-left' :
30467                 case 'md-right' :
30468                 case 'wide' :
30469                     
30470                     if(box.length){
30471                         boxes.push(box);
30472                         box = [];
30473                     }
30474                     
30475                     boxes.push([item]);
30476                     
30477                     break;
30478                     
30479                 case 'xs' :
30480                 case 'sm' :
30481                 case 'tall' :
30482                     
30483                     box.push(item);
30484                     
30485                     break;
30486                 default :
30487                     break;
30488                     
30489             }
30490             
30491         }, this);
30492         
30493         if(box.length){
30494             boxes.push(box);
30495             box = [];
30496         }
30497         
30498         var filterPattern = function(box, length)
30499         {
30500             if(!box.length){
30501                 return;
30502             }
30503             
30504             var match = false;
30505             
30506             var pattern = box.slice(0, length);
30507             
30508             var format = [];
30509             
30510             Roo.each(pattern, function(i){
30511                 format.push(i.size);
30512             }, this);
30513             
30514             Roo.each(standard, function(s){
30515                 
30516                 if(String(s) != String(format)){
30517                     return;
30518                 }
30519                 
30520                 match = true;
30521                 return false;
30522                 
30523             }, this);
30524             
30525             if(!match && length == 1){
30526                 return;
30527             }
30528             
30529             if(!match){
30530                 filterPattern(box, length - 1);
30531                 return;
30532             }
30533                 
30534             queue.push(pattern);
30535
30536             box = box.slice(length, box.length);
30537
30538             filterPattern(box, 4);
30539
30540             return;
30541             
30542         }
30543         
30544         Roo.each(boxes, function(box, k){
30545             
30546             if(!box.length){
30547                 return;
30548             }
30549             
30550             if(box.length == 1){
30551                 queue.push(box);
30552                 return;
30553             }
30554             
30555             filterPattern(box, 4);
30556             
30557         }, this);
30558         
30559         this._processVerticalLayoutQueue( queue, isInstant );
30560         
30561     },
30562     
30563 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30564 //    {
30565 //        if ( !items || !items.length ) {
30566 //            return;
30567 //        }
30568 //
30569 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30570 //        
30571 //    },
30572     
30573     _horizontalLayoutItems : function ( items , isInstant)
30574     {
30575         if ( !items || !items.length || items.length < 3) {
30576             return;
30577         }
30578         
30579         items.reverse();
30580         
30581         var eItems = items.slice(0, 3);
30582         
30583         items = items.slice(3, items.length);
30584         
30585         var standard = [
30586             ['xs', 'xs', 'xs', 'wide'],
30587             ['xs', 'xs', 'wide'],
30588             ['xs', 'xs', 'sm'],
30589             ['xs', 'xs', 'xs'],
30590             ['xs', 'wide'],
30591             ['xs', 'sm'],
30592             ['xs', 'xs'],
30593             ['xs'],
30594             
30595             ['sm', 'xs', 'xs'],
30596             ['sm', 'xs'],
30597             ['sm'],
30598             
30599             ['wide', 'xs', 'xs', 'xs'],
30600             ['wide', 'xs', 'xs'],
30601             ['wide', 'xs'],
30602             ['wide'],
30603             
30604             ['wide-thin']
30605         ];
30606         
30607         var queue = [];
30608         
30609         var boxes = [];
30610         
30611         var box = [];
30612         
30613         Roo.each(items, function(item, k){
30614             
30615             switch (item.size) {
30616                 case 'md' :
30617                 case 'md-left' :
30618                 case 'md-right' :
30619                 case 'tall' :
30620                     
30621                     if(box.length){
30622                         boxes.push(box);
30623                         box = [];
30624                     }
30625                     
30626                     boxes.push([item]);
30627                     
30628                     break;
30629                     
30630                 case 'xs' :
30631                 case 'sm' :
30632                 case 'wide' :
30633                 case 'wide-thin' :
30634                     
30635                     box.push(item);
30636                     
30637                     break;
30638                 default :
30639                     break;
30640                     
30641             }
30642             
30643         }, this);
30644         
30645         if(box.length){
30646             boxes.push(box);
30647             box = [];
30648         }
30649         
30650         var filterPattern = function(box, length)
30651         {
30652             if(!box.length){
30653                 return;
30654             }
30655             
30656             var match = false;
30657             
30658             var pattern = box.slice(0, length);
30659             
30660             var format = [];
30661             
30662             Roo.each(pattern, function(i){
30663                 format.push(i.size);
30664             }, this);
30665             
30666             Roo.each(standard, function(s){
30667                 
30668                 if(String(s) != String(format)){
30669                     return;
30670                 }
30671                 
30672                 match = true;
30673                 return false;
30674                 
30675             }, this);
30676             
30677             if(!match && length == 1){
30678                 return;
30679             }
30680             
30681             if(!match){
30682                 filterPattern(box, length - 1);
30683                 return;
30684             }
30685                 
30686             queue.push(pattern);
30687
30688             box = box.slice(length, box.length);
30689
30690             filterPattern(box, 4);
30691
30692             return;
30693             
30694         }
30695         
30696         Roo.each(boxes, function(box, k){
30697             
30698             if(!box.length){
30699                 return;
30700             }
30701             
30702             if(box.length == 1){
30703                 queue.push(box);
30704                 return;
30705             }
30706             
30707             filterPattern(box, 4);
30708             
30709         }, this);
30710         
30711         
30712         var prune = [];
30713         
30714         var pos = this.el.getBox(true);
30715         
30716         var minX = pos.x;
30717         
30718         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30719         
30720         var hit_end = false;
30721         
30722         Roo.each(queue, function(box){
30723             
30724             if(hit_end){
30725                 
30726                 Roo.each(box, function(b){
30727                 
30728                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30729                     b.el.hide();
30730
30731                 }, this);
30732
30733                 return;
30734             }
30735             
30736             var mx = 0;
30737             
30738             Roo.each(box, function(b){
30739                 
30740                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30741                 b.el.show();
30742
30743                 mx = Math.max(mx, b.x);
30744                 
30745             }, this);
30746             
30747             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30748             
30749             if(maxX < minX){
30750                 
30751                 Roo.each(box, function(b){
30752                 
30753                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30754                     b.el.hide();
30755                     
30756                 }, this);
30757                 
30758                 hit_end = true;
30759                 
30760                 return;
30761             }
30762             
30763             prune.push(box);
30764             
30765         }, this);
30766         
30767         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30768     },
30769     
30770     /** Sets position of item in DOM
30771     * @param {Element} item
30772     * @param {Number} x - horizontal position
30773     * @param {Number} y - vertical position
30774     * @param {Boolean} isInstant - disables transitions
30775     */
30776     _processVerticalLayoutQueue : function( queue, isInstant )
30777     {
30778         var pos = this.el.getBox(true);
30779         var x = pos.x;
30780         var y = pos.y;
30781         var maxY = [];
30782         
30783         for (var i = 0; i < this.cols; i++){
30784             maxY[i] = pos.y;
30785         }
30786         
30787         Roo.each(queue, function(box, k){
30788             
30789             var col = k % this.cols;
30790             
30791             Roo.each(box, function(b,kk){
30792                 
30793                 b.el.position('absolute');
30794                 
30795                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30796                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30797                 
30798                 if(b.size == 'md-left' || b.size == 'md-right'){
30799                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30800                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30801                 }
30802                 
30803                 b.el.setWidth(width);
30804                 b.el.setHeight(height);
30805                 // iframe?
30806                 b.el.select('iframe',true).setSize(width,height);
30807                 
30808             }, this);
30809             
30810             for (var i = 0; i < this.cols; i++){
30811                 
30812                 if(maxY[i] < maxY[col]){
30813                     col = i;
30814                     continue;
30815                 }
30816                 
30817                 col = Math.min(col, i);
30818                 
30819             }
30820             
30821             x = pos.x + col * (this.colWidth + this.padWidth);
30822             
30823             y = maxY[col];
30824             
30825             var positions = [];
30826             
30827             switch (box.length){
30828                 case 1 :
30829                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30830                     break;
30831                 case 2 :
30832                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30833                     break;
30834                 case 3 :
30835                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30836                     break;
30837                 case 4 :
30838                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30839                     break;
30840                 default :
30841                     break;
30842             }
30843             
30844             Roo.each(box, function(b,kk){
30845                 
30846                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30847                 
30848                 var sz = b.el.getSize();
30849                 
30850                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30851                 
30852             }, this);
30853             
30854         }, this);
30855         
30856         var mY = 0;
30857         
30858         for (var i = 0; i < this.cols; i++){
30859             mY = Math.max(mY, maxY[i]);
30860         }
30861         
30862         this.el.setHeight(mY - pos.y);
30863         
30864     },
30865     
30866 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30867 //    {
30868 //        var pos = this.el.getBox(true);
30869 //        var x = pos.x;
30870 //        var y = pos.y;
30871 //        var maxX = pos.right;
30872 //        
30873 //        var maxHeight = 0;
30874 //        
30875 //        Roo.each(items, function(item, k){
30876 //            
30877 //            var c = k % 2;
30878 //            
30879 //            item.el.position('absolute');
30880 //                
30881 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30882 //
30883 //            item.el.setWidth(width);
30884 //
30885 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30886 //
30887 //            item.el.setHeight(height);
30888 //            
30889 //            if(c == 0){
30890 //                item.el.setXY([x, y], isInstant ? false : true);
30891 //            } else {
30892 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30893 //            }
30894 //            
30895 //            y = y + height + this.alternativePadWidth;
30896 //            
30897 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30898 //            
30899 //        }, this);
30900 //        
30901 //        this.el.setHeight(maxHeight);
30902 //        
30903 //    },
30904     
30905     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30906     {
30907         var pos = this.el.getBox(true);
30908         
30909         var minX = pos.x;
30910         var minY = pos.y;
30911         
30912         var maxX = pos.right;
30913         
30914         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30915         
30916         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30917         
30918         Roo.each(queue, function(box, k){
30919             
30920             Roo.each(box, function(b, kk){
30921                 
30922                 b.el.position('absolute');
30923                 
30924                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30925                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30926                 
30927                 if(b.size == 'md-left' || b.size == 'md-right'){
30928                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30929                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30930                 }
30931                 
30932                 b.el.setWidth(width);
30933                 b.el.setHeight(height);
30934                 
30935             }, this);
30936             
30937             if(!box.length){
30938                 return;
30939             }
30940             
30941             var positions = [];
30942             
30943             switch (box.length){
30944                 case 1 :
30945                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30946                     break;
30947                 case 2 :
30948                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30949                     break;
30950                 case 3 :
30951                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30952                     break;
30953                 case 4 :
30954                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30955                     break;
30956                 default :
30957                     break;
30958             }
30959             
30960             Roo.each(box, function(b,kk){
30961                 
30962                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30963                 
30964                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30965                 
30966             }, this);
30967             
30968         }, this);
30969         
30970     },
30971     
30972     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30973     {
30974         Roo.each(eItems, function(b,k){
30975             
30976             b.size = (k == 0) ? 'sm' : 'xs';
30977             b.x = (k == 0) ? 2 : 1;
30978             b.y = (k == 0) ? 2 : 1;
30979             
30980             b.el.position('absolute');
30981             
30982             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30983                 
30984             b.el.setWidth(width);
30985             
30986             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30987             
30988             b.el.setHeight(height);
30989             
30990         }, this);
30991
30992         var positions = [];
30993         
30994         positions.push({
30995             x : maxX - this.unitWidth * 2 - this.gutter,
30996             y : minY
30997         });
30998         
30999         positions.push({
31000             x : maxX - this.unitWidth,
31001             y : minY + (this.unitWidth + this.gutter) * 2
31002         });
31003         
31004         positions.push({
31005             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31006             y : minY
31007         });
31008         
31009         Roo.each(eItems, function(b,k){
31010             
31011             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31012
31013         }, this);
31014         
31015     },
31016     
31017     getVerticalOneBoxColPositions : function(x, y, box)
31018     {
31019         var pos = [];
31020         
31021         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31022         
31023         if(box[0].size == 'md-left'){
31024             rand = 0;
31025         }
31026         
31027         if(box[0].size == 'md-right'){
31028             rand = 1;
31029         }
31030         
31031         pos.push({
31032             x : x + (this.unitWidth + this.gutter) * rand,
31033             y : y
31034         });
31035         
31036         return pos;
31037     },
31038     
31039     getVerticalTwoBoxColPositions : function(x, y, box)
31040     {
31041         var pos = [];
31042         
31043         if(box[0].size == 'xs'){
31044             
31045             pos.push({
31046                 x : x,
31047                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31048             });
31049
31050             pos.push({
31051                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31052                 y : y
31053             });
31054             
31055             return pos;
31056             
31057         }
31058         
31059         pos.push({
31060             x : x,
31061             y : y
31062         });
31063
31064         pos.push({
31065             x : x + (this.unitWidth + this.gutter) * 2,
31066             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31067         });
31068         
31069         return pos;
31070         
31071     },
31072     
31073     getVerticalThreeBoxColPositions : function(x, y, box)
31074     {
31075         var pos = [];
31076         
31077         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31078             
31079             pos.push({
31080                 x : x,
31081                 y : y
31082             });
31083
31084             pos.push({
31085                 x : x + (this.unitWidth + this.gutter) * 1,
31086                 y : y
31087             });
31088             
31089             pos.push({
31090                 x : x + (this.unitWidth + this.gutter) * 2,
31091                 y : y
31092             });
31093             
31094             return pos;
31095             
31096         }
31097         
31098         if(box[0].size == 'xs' && box[1].size == 'xs'){
31099             
31100             pos.push({
31101                 x : x,
31102                 y : y
31103             });
31104
31105             pos.push({
31106                 x : x,
31107                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31108             });
31109             
31110             pos.push({
31111                 x : x + (this.unitWidth + this.gutter) * 1,
31112                 y : y
31113             });
31114             
31115             return pos;
31116             
31117         }
31118         
31119         pos.push({
31120             x : x,
31121             y : y
31122         });
31123
31124         pos.push({
31125             x : x + (this.unitWidth + this.gutter) * 2,
31126             y : y
31127         });
31128
31129         pos.push({
31130             x : x + (this.unitWidth + this.gutter) * 2,
31131             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31132         });
31133             
31134         return pos;
31135         
31136     },
31137     
31138     getVerticalFourBoxColPositions : function(x, y, box)
31139     {
31140         var pos = [];
31141         
31142         if(box[0].size == 'xs'){
31143             
31144             pos.push({
31145                 x : x,
31146                 y : y
31147             });
31148
31149             pos.push({
31150                 x : x,
31151                 y : y + (this.unitHeight + this.gutter) * 1
31152             });
31153             
31154             pos.push({
31155                 x : x,
31156                 y : y + (this.unitHeight + this.gutter) * 2
31157             });
31158             
31159             pos.push({
31160                 x : x + (this.unitWidth + this.gutter) * 1,
31161                 y : y
31162             });
31163             
31164             return pos;
31165             
31166         }
31167         
31168         pos.push({
31169             x : x,
31170             y : y
31171         });
31172
31173         pos.push({
31174             x : x + (this.unitWidth + this.gutter) * 2,
31175             y : y
31176         });
31177
31178         pos.push({
31179             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31180             y : y + (this.unitHeight + this.gutter) * 1
31181         });
31182
31183         pos.push({
31184             x : x + (this.unitWidth + this.gutter) * 2,
31185             y : y + (this.unitWidth + this.gutter) * 2
31186         });
31187
31188         return pos;
31189         
31190     },
31191     
31192     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31193     {
31194         var pos = [];
31195         
31196         if(box[0].size == 'md-left'){
31197             pos.push({
31198                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31199                 y : minY
31200             });
31201             
31202             return pos;
31203         }
31204         
31205         if(box[0].size == 'md-right'){
31206             pos.push({
31207                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31208                 y : minY + (this.unitWidth + this.gutter) * 1
31209             });
31210             
31211             return pos;
31212         }
31213         
31214         var rand = Math.floor(Math.random() * (4 - box[0].y));
31215         
31216         pos.push({
31217             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31218             y : minY + (this.unitWidth + this.gutter) * rand
31219         });
31220         
31221         return pos;
31222         
31223     },
31224     
31225     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31226     {
31227         var pos = [];
31228         
31229         if(box[0].size == 'xs'){
31230             
31231             pos.push({
31232                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31233                 y : minY
31234             });
31235
31236             pos.push({
31237                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31238                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31239             });
31240             
31241             return pos;
31242             
31243         }
31244         
31245         pos.push({
31246             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31247             y : minY
31248         });
31249
31250         pos.push({
31251             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31252             y : minY + (this.unitWidth + this.gutter) * 2
31253         });
31254         
31255         return pos;
31256         
31257     },
31258     
31259     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31260     {
31261         var pos = [];
31262         
31263         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31264             
31265             pos.push({
31266                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31267                 y : minY
31268             });
31269
31270             pos.push({
31271                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31272                 y : minY + (this.unitWidth + this.gutter) * 1
31273             });
31274             
31275             pos.push({
31276                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31277                 y : minY + (this.unitWidth + this.gutter) * 2
31278             });
31279             
31280             return pos;
31281             
31282         }
31283         
31284         if(box[0].size == 'xs' && box[1].size == 'xs'){
31285             
31286             pos.push({
31287                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31288                 y : minY
31289             });
31290
31291             pos.push({
31292                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31293                 y : minY
31294             });
31295             
31296             pos.push({
31297                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31298                 y : minY + (this.unitWidth + this.gutter) * 1
31299             });
31300             
31301             return pos;
31302             
31303         }
31304         
31305         pos.push({
31306             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31307             y : minY
31308         });
31309
31310         pos.push({
31311             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31312             y : minY + (this.unitWidth + this.gutter) * 2
31313         });
31314
31315         pos.push({
31316             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31317             y : minY + (this.unitWidth + this.gutter) * 2
31318         });
31319             
31320         return pos;
31321         
31322     },
31323     
31324     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31325     {
31326         var pos = [];
31327         
31328         if(box[0].size == 'xs'){
31329             
31330             pos.push({
31331                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31332                 y : minY
31333             });
31334
31335             pos.push({
31336                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31337                 y : minY
31338             });
31339             
31340             pos.push({
31341                 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),
31342                 y : minY
31343             });
31344             
31345             pos.push({
31346                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31347                 y : minY + (this.unitWidth + this.gutter) * 1
31348             });
31349             
31350             return pos;
31351             
31352         }
31353         
31354         pos.push({
31355             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31356             y : minY
31357         });
31358         
31359         pos.push({
31360             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31361             y : minY + (this.unitWidth + this.gutter) * 2
31362         });
31363         
31364         pos.push({
31365             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31366             y : minY + (this.unitWidth + this.gutter) * 2
31367         });
31368         
31369         pos.push({
31370             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),
31371             y : minY + (this.unitWidth + this.gutter) * 2
31372         });
31373
31374         return pos;
31375         
31376     },
31377     
31378     /**
31379     * remove a Masonry Brick
31380     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31381     */
31382     removeBrick : function(brick_id)
31383     {
31384         if (!brick_id) {
31385             return;
31386         }
31387         
31388         for (var i = 0; i<this.bricks.length; i++) {
31389             if (this.bricks[i].id == brick_id) {
31390                 this.bricks.splice(i,1);
31391                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31392                 this.initial();
31393             }
31394         }
31395     },
31396     
31397     /**
31398     * adds a Masonry Brick
31399     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31400     */
31401     addBrick : function(cfg)
31402     {
31403         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31404         //this.register(cn);
31405         cn.parentId = this.id;
31406         cn.onRender(this.el, null);
31407         return cn;
31408     },
31409     
31410     /**
31411     * register a Masonry Brick
31412     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31413     */
31414     
31415     register : function(brick)
31416     {
31417         this.bricks.push(brick);
31418         brick.masonryId = this.id;
31419     },
31420     
31421     /**
31422     * clear all the Masonry Brick
31423     */
31424     clearAll : function()
31425     {
31426         this.bricks = [];
31427         //this.getChildContainer().dom.innerHTML = "";
31428         this.el.dom.innerHTML = '';
31429     },
31430     
31431     getSelected : function()
31432     {
31433         if (!this.selectedBrick) {
31434             return false;
31435         }
31436         
31437         return this.selectedBrick;
31438     }
31439 });
31440
31441 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31442     
31443     groups: {},
31444      /**
31445     * register a Masonry Layout
31446     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31447     */
31448     
31449     register : function(layout)
31450     {
31451         this.groups[layout.id] = layout;
31452     },
31453     /**
31454     * fetch a  Masonry Layout based on the masonry layout ID
31455     * @param {string} the masonry layout to add
31456     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31457     */
31458     
31459     get: function(layout_id) {
31460         if (typeof(this.groups[layout_id]) == 'undefined') {
31461             return false;
31462         }
31463         return this.groups[layout_id] ;
31464     }
31465     
31466     
31467     
31468 });
31469
31470  
31471
31472  /**
31473  *
31474  * This is based on 
31475  * http://masonry.desandro.com
31476  *
31477  * The idea is to render all the bricks based on vertical width...
31478  *
31479  * The original code extends 'outlayer' - we might need to use that....
31480  * 
31481  */
31482
31483
31484 /**
31485  * @class Roo.bootstrap.LayoutMasonryAuto
31486  * @extends Roo.bootstrap.Component
31487  * Bootstrap Layout Masonry class
31488  * 
31489  * @constructor
31490  * Create a new Element
31491  * @param {Object} config The config object
31492  */
31493
31494 Roo.bootstrap.LayoutMasonryAuto = function(config){
31495     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31496 };
31497
31498 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31499     
31500       /**
31501      * @cfg {Boolean} isFitWidth  - resize the width..
31502      */   
31503     isFitWidth : false,  // options..
31504     /**
31505      * @cfg {Boolean} isOriginLeft = left align?
31506      */   
31507     isOriginLeft : true,
31508     /**
31509      * @cfg {Boolean} isOriginTop = top align?
31510      */   
31511     isOriginTop : false,
31512     /**
31513      * @cfg {Boolean} isLayoutInstant = no animation?
31514      */   
31515     isLayoutInstant : false, // needed?
31516     /**
31517      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31518      */   
31519     isResizingContainer : true,
31520     /**
31521      * @cfg {Number} columnWidth  width of the columns 
31522      */   
31523     
31524     columnWidth : 0,
31525     
31526     /**
31527      * @cfg {Number} maxCols maximum number of columns
31528      */   
31529     
31530     maxCols: 0,
31531     /**
31532      * @cfg {Number} padHeight padding below box..
31533      */   
31534     
31535     padHeight : 10, 
31536     
31537     /**
31538      * @cfg {Boolean} isAutoInitial defalut true
31539      */   
31540     
31541     isAutoInitial : true, 
31542     
31543     // private?
31544     gutter : 0,
31545     
31546     containerWidth: 0,
31547     initialColumnWidth : 0,
31548     currentSize : null,
31549     
31550     colYs : null, // array.
31551     maxY : 0,
31552     padWidth: 10,
31553     
31554     
31555     tag: 'div',
31556     cls: '',
31557     bricks: null, //CompositeElement
31558     cols : 0, // array?
31559     // element : null, // wrapped now this.el
31560     _isLayoutInited : null, 
31561     
31562     
31563     getAutoCreate : function(){
31564         
31565         var cfg = {
31566             tag: this.tag,
31567             cls: 'blog-masonary-wrapper ' + this.cls,
31568             cn : {
31569                 cls : 'mas-boxes masonary'
31570             }
31571         };
31572         
31573         return cfg;
31574     },
31575     
31576     getChildContainer: function( )
31577     {
31578         if (this.boxesEl) {
31579             return this.boxesEl;
31580         }
31581         
31582         this.boxesEl = this.el.select('.mas-boxes').first();
31583         
31584         return this.boxesEl;
31585     },
31586     
31587     
31588     initEvents : function()
31589     {
31590         var _this = this;
31591         
31592         if(this.isAutoInitial){
31593             Roo.log('hook children rendered');
31594             this.on('childrenrendered', function() {
31595                 Roo.log('children rendered');
31596                 _this.initial();
31597             } ,this);
31598         }
31599         
31600     },
31601     
31602     initial : function()
31603     {
31604         this.reloadItems();
31605
31606         this.currentSize = this.el.getBox(true);
31607
31608         /// was window resize... - let's see if this works..
31609         Roo.EventManager.onWindowResize(this.resize, this); 
31610
31611         if(!this.isAutoInitial){
31612             this.layout();
31613             return;
31614         }
31615         
31616         this.layout.defer(500,this);
31617     },
31618     
31619     reloadItems: function()
31620     {
31621         this.bricks = this.el.select('.masonry-brick', true);
31622         
31623         this.bricks.each(function(b) {
31624             //Roo.log(b.getSize());
31625             if (!b.attr('originalwidth')) {
31626                 b.attr('originalwidth',  b.getSize().width);
31627             }
31628             
31629         });
31630         
31631         Roo.log(this.bricks.elements.length);
31632     },
31633     
31634     resize : function()
31635     {
31636         Roo.log('resize');
31637         var cs = this.el.getBox(true);
31638         
31639         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31640             Roo.log("no change in with or X");
31641             return;
31642         }
31643         this.currentSize = cs;
31644         this.layout();
31645     },
31646     
31647     layout : function()
31648     {
31649          Roo.log('layout');
31650         this._resetLayout();
31651         //this._manageStamps();
31652       
31653         // don't animate first layout
31654         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31655         this.layoutItems( isInstant );
31656       
31657         // flag for initalized
31658         this._isLayoutInited = true;
31659     },
31660     
31661     layoutItems : function( isInstant )
31662     {
31663         //var items = this._getItemsForLayout( this.items );
31664         // original code supports filtering layout items.. we just ignore it..
31665         
31666         this._layoutItems( this.bricks , isInstant );
31667       
31668         this._postLayout();
31669     },
31670     _layoutItems : function ( items , isInstant)
31671     {
31672        //this.fireEvent( 'layout', this, items );
31673     
31674
31675         if ( !items || !items.elements.length ) {
31676           // no items, emit event with empty array
31677             return;
31678         }
31679
31680         var queue = [];
31681         items.each(function(item) {
31682             Roo.log("layout item");
31683             Roo.log(item);
31684             // get x/y object from method
31685             var position = this._getItemLayoutPosition( item );
31686             // enqueue
31687             position.item = item;
31688             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31689             queue.push( position );
31690         }, this);
31691       
31692         this._processLayoutQueue( queue );
31693     },
31694     /** Sets position of item in DOM
31695     * @param {Element} item
31696     * @param {Number} x - horizontal position
31697     * @param {Number} y - vertical position
31698     * @param {Boolean} isInstant - disables transitions
31699     */
31700     _processLayoutQueue : function( queue )
31701     {
31702         for ( var i=0, len = queue.length; i < len; i++ ) {
31703             var obj = queue[i];
31704             obj.item.position('absolute');
31705             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31706         }
31707     },
31708       
31709     
31710     /**
31711     * Any logic you want to do after each layout,
31712     * i.e. size the container
31713     */
31714     _postLayout : function()
31715     {
31716         this.resizeContainer();
31717     },
31718     
31719     resizeContainer : function()
31720     {
31721         if ( !this.isResizingContainer ) {
31722             return;
31723         }
31724         var size = this._getContainerSize();
31725         if ( size ) {
31726             this.el.setSize(size.width,size.height);
31727             this.boxesEl.setSize(size.width,size.height);
31728         }
31729     },
31730     
31731     
31732     
31733     _resetLayout : function()
31734     {
31735         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31736         this.colWidth = this.el.getWidth();
31737         //this.gutter = this.el.getWidth(); 
31738         
31739         this.measureColumns();
31740
31741         // reset column Y
31742         var i = this.cols;
31743         this.colYs = [];
31744         while (i--) {
31745             this.colYs.push( 0 );
31746         }
31747     
31748         this.maxY = 0;
31749     },
31750
31751     measureColumns : function()
31752     {
31753         this.getContainerWidth();
31754       // if columnWidth is 0, default to outerWidth of first item
31755         if ( !this.columnWidth ) {
31756             var firstItem = this.bricks.first();
31757             Roo.log(firstItem);
31758             this.columnWidth  = this.containerWidth;
31759             if (firstItem && firstItem.attr('originalwidth') ) {
31760                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31761             }
31762             // columnWidth fall back to item of first element
31763             Roo.log("set column width?");
31764                         this.initialColumnWidth = this.columnWidth  ;
31765
31766             // if first elem has no width, default to size of container
31767             
31768         }
31769         
31770         
31771         if (this.initialColumnWidth) {
31772             this.columnWidth = this.initialColumnWidth;
31773         }
31774         
31775         
31776             
31777         // column width is fixed at the top - however if container width get's smaller we should
31778         // reduce it...
31779         
31780         // this bit calcs how man columns..
31781             
31782         var columnWidth = this.columnWidth += this.gutter;
31783       
31784         // calculate columns
31785         var containerWidth = this.containerWidth + this.gutter;
31786         
31787         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31788         // fix rounding errors, typically with gutters
31789         var excess = columnWidth - containerWidth % columnWidth;
31790         
31791         
31792         // if overshoot is less than a pixel, round up, otherwise floor it
31793         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31794         cols = Math[ mathMethod ]( cols );
31795         this.cols = Math.max( cols, 1 );
31796         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31797         
31798          // padding positioning..
31799         var totalColWidth = this.cols * this.columnWidth;
31800         var padavail = this.containerWidth - totalColWidth;
31801         // so for 2 columns - we need 3 'pads'
31802         
31803         var padNeeded = (1+this.cols) * this.padWidth;
31804         
31805         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31806         
31807         this.columnWidth += padExtra
31808         //this.padWidth = Math.floor(padavail /  ( this.cols));
31809         
31810         // adjust colum width so that padding is fixed??
31811         
31812         // we have 3 columns ... total = width * 3
31813         // we have X left over... that should be used by 
31814         
31815         //if (this.expandC) {
31816             
31817         //}
31818         
31819         
31820         
31821     },
31822     
31823     getContainerWidth : function()
31824     {
31825        /* // container is parent if fit width
31826         var container = this.isFitWidth ? this.element.parentNode : this.element;
31827         // check that this.size and size are there
31828         // IE8 triggers resize on body size change, so they might not be
31829         
31830         var size = getSize( container );  //FIXME
31831         this.containerWidth = size && size.innerWidth; //FIXME
31832         */
31833          
31834         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31835         
31836     },
31837     
31838     _getItemLayoutPosition : function( item )  // what is item?
31839     {
31840         // we resize the item to our columnWidth..
31841       
31842         item.setWidth(this.columnWidth);
31843         item.autoBoxAdjust  = false;
31844         
31845         var sz = item.getSize();
31846  
31847         // how many columns does this brick span
31848         var remainder = this.containerWidth % this.columnWidth;
31849         
31850         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31851         // round if off by 1 pixel, otherwise use ceil
31852         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31853         colSpan = Math.min( colSpan, this.cols );
31854         
31855         // normally this should be '1' as we dont' currently allow multi width columns..
31856         
31857         var colGroup = this._getColGroup( colSpan );
31858         // get the minimum Y value from the columns
31859         var minimumY = Math.min.apply( Math, colGroup );
31860         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31861         
31862         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31863          
31864         // position the brick
31865         var position = {
31866             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31867             y: this.currentSize.y + minimumY + this.padHeight
31868         };
31869         
31870         Roo.log(position);
31871         // apply setHeight to necessary columns
31872         var setHeight = minimumY + sz.height + this.padHeight;
31873         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31874         
31875         var setSpan = this.cols + 1 - colGroup.length;
31876         for ( var i = 0; i < setSpan; i++ ) {
31877           this.colYs[ shortColIndex + i ] = setHeight ;
31878         }
31879       
31880         return position;
31881     },
31882     
31883     /**
31884      * @param {Number} colSpan - number of columns the element spans
31885      * @returns {Array} colGroup
31886      */
31887     _getColGroup : function( colSpan )
31888     {
31889         if ( colSpan < 2 ) {
31890           // if brick spans only one column, use all the column Ys
31891           return this.colYs;
31892         }
31893       
31894         var colGroup = [];
31895         // how many different places could this brick fit horizontally
31896         var groupCount = this.cols + 1 - colSpan;
31897         // for each group potential horizontal position
31898         for ( var i = 0; i < groupCount; i++ ) {
31899           // make an array of colY values for that one group
31900           var groupColYs = this.colYs.slice( i, i + colSpan );
31901           // and get the max value of the array
31902           colGroup[i] = Math.max.apply( Math, groupColYs );
31903         }
31904         return colGroup;
31905     },
31906     /*
31907     _manageStamp : function( stamp )
31908     {
31909         var stampSize =  stamp.getSize();
31910         var offset = stamp.getBox();
31911         // get the columns that this stamp affects
31912         var firstX = this.isOriginLeft ? offset.x : offset.right;
31913         var lastX = firstX + stampSize.width;
31914         var firstCol = Math.floor( firstX / this.columnWidth );
31915         firstCol = Math.max( 0, firstCol );
31916         
31917         var lastCol = Math.floor( lastX / this.columnWidth );
31918         // lastCol should not go over if multiple of columnWidth #425
31919         lastCol -= lastX % this.columnWidth ? 0 : 1;
31920         lastCol = Math.min( this.cols - 1, lastCol );
31921         
31922         // set colYs to bottom of the stamp
31923         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31924             stampSize.height;
31925             
31926         for ( var i = firstCol; i <= lastCol; i++ ) {
31927           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31928         }
31929     },
31930     */
31931     
31932     _getContainerSize : function()
31933     {
31934         this.maxY = Math.max.apply( Math, this.colYs );
31935         var size = {
31936             height: this.maxY
31937         };
31938       
31939         if ( this.isFitWidth ) {
31940             size.width = this._getContainerFitWidth();
31941         }
31942       
31943         return size;
31944     },
31945     
31946     _getContainerFitWidth : function()
31947     {
31948         var unusedCols = 0;
31949         // count unused columns
31950         var i = this.cols;
31951         while ( --i ) {
31952           if ( this.colYs[i] !== 0 ) {
31953             break;
31954           }
31955           unusedCols++;
31956         }
31957         // fit container to columns that have been used
31958         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31959     },
31960     
31961     needsResizeLayout : function()
31962     {
31963         var previousWidth = this.containerWidth;
31964         this.getContainerWidth();
31965         return previousWidth !== this.containerWidth;
31966     }
31967  
31968 });
31969
31970  
31971
31972  /*
31973  * - LGPL
31974  *
31975  * element
31976  * 
31977  */
31978
31979 /**
31980  * @class Roo.bootstrap.MasonryBrick
31981  * @extends Roo.bootstrap.Component
31982  * Bootstrap MasonryBrick class
31983  * 
31984  * @constructor
31985  * Create a new MasonryBrick
31986  * @param {Object} config The config object
31987  */
31988
31989 Roo.bootstrap.MasonryBrick = function(config){
31990     
31991     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31992     
31993     Roo.bootstrap.MasonryBrick.register(this);
31994     
31995     this.addEvents({
31996         // raw events
31997         /**
31998          * @event click
31999          * When a MasonryBrick is clcik
32000          * @param {Roo.bootstrap.MasonryBrick} this
32001          * @param {Roo.EventObject} e
32002          */
32003         "click" : true
32004     });
32005 };
32006
32007 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32008     
32009     /**
32010      * @cfg {String} title
32011      */   
32012     title : '',
32013     /**
32014      * @cfg {String} html
32015      */   
32016     html : '',
32017     /**
32018      * @cfg {String} bgimage
32019      */   
32020     bgimage : '',
32021     /**
32022      * @cfg {String} videourl
32023      */   
32024     videourl : '',
32025     /**
32026      * @cfg {String} cls
32027      */   
32028     cls : '',
32029     /**
32030      * @cfg {String} href
32031      */   
32032     href : '',
32033     /**
32034      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32035      */   
32036     size : 'xs',
32037     
32038     /**
32039      * @cfg {String} placetitle (center|bottom)
32040      */   
32041     placetitle : '',
32042     
32043     /**
32044      * @cfg {Boolean} isFitContainer defalut true
32045      */   
32046     isFitContainer : true, 
32047     
32048     /**
32049      * @cfg {Boolean} preventDefault defalut false
32050      */   
32051     preventDefault : false, 
32052     
32053     /**
32054      * @cfg {Boolean} inverse defalut false
32055      */   
32056     maskInverse : false, 
32057     
32058     getAutoCreate : function()
32059     {
32060         if(!this.isFitContainer){
32061             return this.getSplitAutoCreate();
32062         }
32063         
32064         var cls = 'masonry-brick masonry-brick-full';
32065         
32066         if(this.href.length){
32067             cls += ' masonry-brick-link';
32068         }
32069         
32070         if(this.bgimage.length){
32071             cls += ' masonry-brick-image';
32072         }
32073         
32074         if(this.maskInverse){
32075             cls += ' mask-inverse';
32076         }
32077         
32078         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32079             cls += ' enable-mask';
32080         }
32081         
32082         if(this.size){
32083             cls += ' masonry-' + this.size + '-brick';
32084         }
32085         
32086         if(this.placetitle.length){
32087             
32088             switch (this.placetitle) {
32089                 case 'center' :
32090                     cls += ' masonry-center-title';
32091                     break;
32092                 case 'bottom' :
32093                     cls += ' masonry-bottom-title';
32094                     break;
32095                 default:
32096                     break;
32097             }
32098             
32099         } else {
32100             if(!this.html.length && !this.bgimage.length){
32101                 cls += ' masonry-center-title';
32102             }
32103
32104             if(!this.html.length && this.bgimage.length){
32105                 cls += ' masonry-bottom-title';
32106             }
32107         }
32108         
32109         if(this.cls){
32110             cls += ' ' + this.cls;
32111         }
32112         
32113         var cfg = {
32114             tag: (this.href.length) ? 'a' : 'div',
32115             cls: cls,
32116             cn: [
32117                 {
32118                     tag: 'div',
32119                     cls: 'masonry-brick-mask'
32120                 },
32121                 {
32122                     tag: 'div',
32123                     cls: 'masonry-brick-paragraph',
32124                     cn: []
32125                 }
32126             ]
32127         };
32128         
32129         if(this.href.length){
32130             cfg.href = this.href;
32131         }
32132         
32133         var cn = cfg.cn[1].cn;
32134         
32135         if(this.title.length){
32136             cn.push({
32137                 tag: 'h4',
32138                 cls: 'masonry-brick-title',
32139                 html: this.title
32140             });
32141         }
32142         
32143         if(this.html.length){
32144             cn.push({
32145                 tag: 'p',
32146                 cls: 'masonry-brick-text',
32147                 html: this.html
32148             });
32149         }
32150         
32151         if (!this.title.length && !this.html.length) {
32152             cfg.cn[1].cls += ' hide';
32153         }
32154         
32155         if(this.bgimage.length){
32156             cfg.cn.push({
32157                 tag: 'img',
32158                 cls: 'masonry-brick-image-view',
32159                 src: this.bgimage
32160             });
32161         }
32162         
32163         if(this.videourl.length){
32164             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32165             // youtube support only?
32166             cfg.cn.push({
32167                 tag: 'iframe',
32168                 cls: 'masonry-brick-image-view',
32169                 src: vurl,
32170                 frameborder : 0,
32171                 allowfullscreen : true
32172             });
32173         }
32174         
32175         return cfg;
32176         
32177     },
32178     
32179     getSplitAutoCreate : function()
32180     {
32181         var cls = 'masonry-brick masonry-brick-split';
32182         
32183         if(this.href.length){
32184             cls += ' masonry-brick-link';
32185         }
32186         
32187         if(this.bgimage.length){
32188             cls += ' masonry-brick-image';
32189         }
32190         
32191         if(this.size){
32192             cls += ' masonry-' + this.size + '-brick';
32193         }
32194         
32195         switch (this.placetitle) {
32196             case 'center' :
32197                 cls += ' masonry-center-title';
32198                 break;
32199             case 'bottom' :
32200                 cls += ' masonry-bottom-title';
32201                 break;
32202             default:
32203                 if(!this.bgimage.length){
32204                     cls += ' masonry-center-title';
32205                 }
32206
32207                 if(this.bgimage.length){
32208                     cls += ' masonry-bottom-title';
32209                 }
32210                 break;
32211         }
32212         
32213         if(this.cls){
32214             cls += ' ' + this.cls;
32215         }
32216         
32217         var cfg = {
32218             tag: (this.href.length) ? 'a' : 'div',
32219             cls: cls,
32220             cn: [
32221                 {
32222                     tag: 'div',
32223                     cls: 'masonry-brick-split-head',
32224                     cn: [
32225                         {
32226                             tag: 'div',
32227                             cls: 'masonry-brick-paragraph',
32228                             cn: []
32229                         }
32230                     ]
32231                 },
32232                 {
32233                     tag: 'div',
32234                     cls: 'masonry-brick-split-body',
32235                     cn: []
32236                 }
32237             ]
32238         };
32239         
32240         if(this.href.length){
32241             cfg.href = this.href;
32242         }
32243         
32244         if(this.title.length){
32245             cfg.cn[0].cn[0].cn.push({
32246                 tag: 'h4',
32247                 cls: 'masonry-brick-title',
32248                 html: this.title
32249             });
32250         }
32251         
32252         if(this.html.length){
32253             cfg.cn[1].cn.push({
32254                 tag: 'p',
32255                 cls: 'masonry-brick-text',
32256                 html: this.html
32257             });
32258         }
32259
32260         if(this.bgimage.length){
32261             cfg.cn[0].cn.push({
32262                 tag: 'img',
32263                 cls: 'masonry-brick-image-view',
32264                 src: this.bgimage
32265             });
32266         }
32267         
32268         if(this.videourl.length){
32269             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32270             // youtube support only?
32271             cfg.cn[0].cn.cn.push({
32272                 tag: 'iframe',
32273                 cls: 'masonry-brick-image-view',
32274                 src: vurl,
32275                 frameborder : 0,
32276                 allowfullscreen : true
32277             });
32278         }
32279         
32280         return cfg;
32281     },
32282     
32283     initEvents: function() 
32284     {
32285         switch (this.size) {
32286             case 'xs' :
32287                 this.x = 1;
32288                 this.y = 1;
32289                 break;
32290             case 'sm' :
32291                 this.x = 2;
32292                 this.y = 2;
32293                 break;
32294             case 'md' :
32295             case 'md-left' :
32296             case 'md-right' :
32297                 this.x = 3;
32298                 this.y = 3;
32299                 break;
32300             case 'tall' :
32301                 this.x = 2;
32302                 this.y = 3;
32303                 break;
32304             case 'wide' :
32305                 this.x = 3;
32306                 this.y = 2;
32307                 break;
32308             case 'wide-thin' :
32309                 this.x = 3;
32310                 this.y = 1;
32311                 break;
32312                         
32313             default :
32314                 break;
32315         }
32316         
32317         if(Roo.isTouch){
32318             this.el.on('touchstart', this.onTouchStart, this);
32319             this.el.on('touchmove', this.onTouchMove, this);
32320             this.el.on('touchend', this.onTouchEnd, this);
32321             this.el.on('contextmenu', this.onContextMenu, this);
32322         } else {
32323             this.el.on('mouseenter'  ,this.enter, this);
32324             this.el.on('mouseleave', this.leave, this);
32325             this.el.on('click', this.onClick, this);
32326         }
32327         
32328         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32329             this.parent().bricks.push(this);   
32330         }
32331         
32332     },
32333     
32334     onClick: function(e, el)
32335     {
32336         var time = this.endTimer - this.startTimer;
32337         // Roo.log(e.preventDefault());
32338         if(Roo.isTouch){
32339             if(time > 1000){
32340                 e.preventDefault();
32341                 return;
32342             }
32343         }
32344         
32345         if(!this.preventDefault){
32346             return;
32347         }
32348         
32349         e.preventDefault();
32350         
32351         if (this.activcClass != '') {
32352             this.selectBrick();
32353         }
32354         
32355         this.fireEvent('click', this);
32356     },
32357     
32358     enter: function(e, el)
32359     {
32360         e.preventDefault();
32361         
32362         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32363             return;
32364         }
32365         
32366         if(this.bgimage.length && this.html.length){
32367             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32368         }
32369     },
32370     
32371     leave: function(e, el)
32372     {
32373         e.preventDefault();
32374         
32375         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32376             return;
32377         }
32378         
32379         if(this.bgimage.length && this.html.length){
32380             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32381         }
32382     },
32383     
32384     onTouchStart: function(e, el)
32385     {
32386 //        e.preventDefault();
32387         
32388         this.touchmoved = false;
32389         
32390         if(!this.isFitContainer){
32391             return;
32392         }
32393         
32394         if(!this.bgimage.length || !this.html.length){
32395             return;
32396         }
32397         
32398         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32399         
32400         this.timer = new Date().getTime();
32401         
32402     },
32403     
32404     onTouchMove: function(e, el)
32405     {
32406         this.touchmoved = true;
32407     },
32408     
32409     onContextMenu : function(e,el)
32410     {
32411         e.preventDefault();
32412         e.stopPropagation();
32413         return false;
32414     },
32415     
32416     onTouchEnd: function(e, el)
32417     {
32418 //        e.preventDefault();
32419         
32420         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32421         
32422             this.leave(e,el);
32423             
32424             return;
32425         }
32426         
32427         if(!this.bgimage.length || !this.html.length){
32428             
32429             if(this.href.length){
32430                 window.location.href = this.href;
32431             }
32432             
32433             return;
32434         }
32435         
32436         if(!this.isFitContainer){
32437             return;
32438         }
32439         
32440         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32441         
32442         window.location.href = this.href;
32443     },
32444     
32445     //selection on single brick only
32446     selectBrick : function() {
32447         
32448         if (!this.parentId) {
32449             return;
32450         }
32451         
32452         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32453         var index = m.selectedBrick.indexOf(this.id);
32454         
32455         if ( index > -1) {
32456             m.selectedBrick.splice(index,1);
32457             this.el.removeClass(this.activeClass);
32458             return;
32459         }
32460         
32461         for(var i = 0; i < m.selectedBrick.length; i++) {
32462             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32463             b.el.removeClass(b.activeClass);
32464         }
32465         
32466         m.selectedBrick = [];
32467         
32468         m.selectedBrick.push(this.id);
32469         this.el.addClass(this.activeClass);
32470         return;
32471     }
32472     
32473 });
32474
32475 Roo.apply(Roo.bootstrap.MasonryBrick, {
32476     
32477     //groups: {},
32478     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32479      /**
32480     * register a Masonry Brick
32481     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32482     */
32483     
32484     register : function(brick)
32485     {
32486         //this.groups[brick.id] = brick;
32487         this.groups.add(brick.id, brick);
32488     },
32489     /**
32490     * fetch a  masonry brick based on the masonry brick ID
32491     * @param {string} the masonry brick to add
32492     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32493     */
32494     
32495     get: function(brick_id) 
32496     {
32497         // if (typeof(this.groups[brick_id]) == 'undefined') {
32498         //     return false;
32499         // }
32500         // return this.groups[brick_id] ;
32501         
32502         if(this.groups.key(brick_id)) {
32503             return this.groups.key(brick_id);
32504         }
32505         
32506         return false;
32507     }
32508     
32509     
32510     
32511 });
32512
32513  /*
32514  * - LGPL
32515  *
32516  * element
32517  * 
32518  */
32519
32520 /**
32521  * @class Roo.bootstrap.Brick
32522  * @extends Roo.bootstrap.Component
32523  * Bootstrap Brick class
32524  * 
32525  * @constructor
32526  * Create a new Brick
32527  * @param {Object} config The config object
32528  */
32529
32530 Roo.bootstrap.Brick = function(config){
32531     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32532     
32533     this.addEvents({
32534         // raw events
32535         /**
32536          * @event click
32537          * When a Brick is click
32538          * @param {Roo.bootstrap.Brick} this
32539          * @param {Roo.EventObject} e
32540          */
32541         "click" : true
32542     });
32543 };
32544
32545 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32546     
32547     /**
32548      * @cfg {String} title
32549      */   
32550     title : '',
32551     /**
32552      * @cfg {String} html
32553      */   
32554     html : '',
32555     /**
32556      * @cfg {String} bgimage
32557      */   
32558     bgimage : '',
32559     /**
32560      * @cfg {String} cls
32561      */   
32562     cls : '',
32563     /**
32564      * @cfg {String} href
32565      */   
32566     href : '',
32567     /**
32568      * @cfg {String} video
32569      */   
32570     video : '',
32571     /**
32572      * @cfg {Boolean} square
32573      */   
32574     square : true,
32575     
32576     getAutoCreate : function()
32577     {
32578         var cls = 'roo-brick';
32579         
32580         if(this.href.length){
32581             cls += ' roo-brick-link';
32582         }
32583         
32584         if(this.bgimage.length){
32585             cls += ' roo-brick-image';
32586         }
32587         
32588         if(!this.html.length && !this.bgimage.length){
32589             cls += ' roo-brick-center-title';
32590         }
32591         
32592         if(!this.html.length && this.bgimage.length){
32593             cls += ' roo-brick-bottom-title';
32594         }
32595         
32596         if(this.cls){
32597             cls += ' ' + this.cls;
32598         }
32599         
32600         var cfg = {
32601             tag: (this.href.length) ? 'a' : 'div',
32602             cls: cls,
32603             cn: [
32604                 {
32605                     tag: 'div',
32606                     cls: 'roo-brick-paragraph',
32607                     cn: []
32608                 }
32609             ]
32610         };
32611         
32612         if(this.href.length){
32613             cfg.href = this.href;
32614         }
32615         
32616         var cn = cfg.cn[0].cn;
32617         
32618         if(this.title.length){
32619             cn.push({
32620                 tag: 'h4',
32621                 cls: 'roo-brick-title',
32622                 html: this.title
32623             });
32624         }
32625         
32626         if(this.html.length){
32627             cn.push({
32628                 tag: 'p',
32629                 cls: 'roo-brick-text',
32630                 html: this.html
32631             });
32632         } else {
32633             cn.cls += ' hide';
32634         }
32635         
32636         if(this.bgimage.length){
32637             cfg.cn.push({
32638                 tag: 'img',
32639                 cls: 'roo-brick-image-view',
32640                 src: this.bgimage
32641             });
32642         }
32643         
32644         return cfg;
32645     },
32646     
32647     initEvents: function() 
32648     {
32649         if(this.title.length || this.html.length){
32650             this.el.on('mouseenter'  ,this.enter, this);
32651             this.el.on('mouseleave', this.leave, this);
32652         }
32653         
32654         Roo.EventManager.onWindowResize(this.resize, this); 
32655         
32656         if(this.bgimage.length){
32657             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32658             this.imageEl.on('load', this.onImageLoad, this);
32659             return;
32660         }
32661         
32662         this.resize();
32663     },
32664     
32665     onImageLoad : function()
32666     {
32667         this.resize();
32668     },
32669     
32670     resize : function()
32671     {
32672         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32673         
32674         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32675         
32676         if(this.bgimage.length){
32677             var image = this.el.select('.roo-brick-image-view', true).first();
32678             
32679             image.setWidth(paragraph.getWidth());
32680             
32681             if(this.square){
32682                 image.setHeight(paragraph.getWidth());
32683             }
32684             
32685             this.el.setHeight(image.getHeight());
32686             paragraph.setHeight(image.getHeight());
32687             
32688         }
32689         
32690     },
32691     
32692     enter: function(e, el)
32693     {
32694         e.preventDefault();
32695         
32696         if(this.bgimage.length){
32697             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32698             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32699         }
32700     },
32701     
32702     leave: function(e, el)
32703     {
32704         e.preventDefault();
32705         
32706         if(this.bgimage.length){
32707             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32708             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32709         }
32710     }
32711     
32712 });
32713
32714  
32715
32716  /*
32717  * - LGPL
32718  *
32719  * Input
32720  * 
32721  */
32722
32723 /**
32724  * @class Roo.bootstrap.NumberField
32725  * @extends Roo.bootstrap.Input
32726  * Bootstrap NumberField class
32727  * 
32728  * 
32729  * 
32730  * 
32731  * @constructor
32732  * Create a new NumberField
32733  * @param {Object} config The config object
32734  */
32735
32736 Roo.bootstrap.NumberField = function(config){
32737     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32738 };
32739
32740 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32741     
32742     /**
32743      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32744      */
32745     allowDecimals : true,
32746     /**
32747      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32748      */
32749     decimalSeparator : ".",
32750     /**
32751      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32752      */
32753     decimalPrecision : 2,
32754     /**
32755      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32756      */
32757     allowNegative : true,
32758     /**
32759      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32760      */
32761     minValue : Number.NEGATIVE_INFINITY,
32762     /**
32763      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32764      */
32765     maxValue : Number.MAX_VALUE,
32766     /**
32767      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32768      */
32769     minText : "The minimum value for this field is {0}",
32770     /**
32771      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32772      */
32773     maxText : "The maximum value for this field is {0}",
32774     /**
32775      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32776      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32777      */
32778     nanText : "{0} is not a valid number",
32779     /**
32780      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32781      */
32782     castInt : true,
32783
32784     // private
32785     initEvents : function()
32786     {   
32787         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32788         
32789         var allowed = "0123456789";
32790         
32791         if(this.allowDecimals){
32792             allowed += this.decimalSeparator;
32793         }
32794         
32795         if(this.allowNegative){
32796             allowed += "-";
32797         }
32798         
32799         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32800         
32801         var keyPress = function(e){
32802             
32803             var k = e.getKey();
32804             
32805             var c = e.getCharCode();
32806             
32807             if(
32808                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32809                     allowed.indexOf(String.fromCharCode(c)) === -1
32810             ){
32811                 e.stopEvent();
32812                 return;
32813             }
32814             
32815             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32816                 return;
32817             }
32818             
32819             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32820                 e.stopEvent();
32821             }
32822         };
32823         
32824         this.el.on("keypress", keyPress, this);
32825     },
32826     
32827     validateValue : function(value)
32828     {
32829         
32830         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32831             return false;
32832         }
32833         
32834         var num = this.parseValue(value);
32835         
32836         if(isNaN(num)){
32837             this.markInvalid(String.format(this.nanText, value));
32838             return false;
32839         }
32840         
32841         if(num < this.minValue){
32842             this.markInvalid(String.format(this.minText, this.minValue));
32843             return false;
32844         }
32845         
32846         if(num > this.maxValue){
32847             this.markInvalid(String.format(this.maxText, this.maxValue));
32848             return false;
32849         }
32850         
32851         return true;
32852     },
32853
32854     getValue : function()
32855     {
32856         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32857     },
32858
32859     parseValue : function(value)
32860     {
32861         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32862         return isNaN(value) ? '' : value;
32863     },
32864
32865     fixPrecision : function(value)
32866     {
32867         var nan = isNaN(value);
32868         
32869         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32870             return nan ? '' : value;
32871         }
32872         return parseFloat(value).toFixed(this.decimalPrecision);
32873     },
32874
32875     setValue : function(v)
32876     {
32877         v = this.fixPrecision(v);
32878         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32879     },
32880
32881     decimalPrecisionFcn : function(v)
32882     {
32883         return Math.floor(v);
32884     },
32885
32886     beforeBlur : function()
32887     {
32888         if(!this.castInt){
32889             return;
32890         }
32891         
32892         var v = this.parseValue(this.getRawValue());
32893         if(v){
32894             this.setValue(v);
32895         }
32896     }
32897     
32898 });
32899
32900  
32901
32902 /*
32903 * Licence: LGPL
32904 */
32905
32906 /**
32907  * @class Roo.bootstrap.DocumentSlider
32908  * @extends Roo.bootstrap.Component
32909  * Bootstrap DocumentSlider class
32910  * 
32911  * @constructor
32912  * Create a new DocumentViewer
32913  * @param {Object} config The config object
32914  */
32915
32916 Roo.bootstrap.DocumentSlider = function(config){
32917     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32918     
32919     this.files = [];
32920     
32921     this.addEvents({
32922         /**
32923          * @event initial
32924          * Fire after initEvent
32925          * @param {Roo.bootstrap.DocumentSlider} this
32926          */
32927         "initial" : true,
32928         /**
32929          * @event update
32930          * Fire after update
32931          * @param {Roo.bootstrap.DocumentSlider} this
32932          */
32933         "update" : true,
32934         /**
32935          * @event click
32936          * Fire after click
32937          * @param {Roo.bootstrap.DocumentSlider} this
32938          */
32939         "click" : true
32940     });
32941 };
32942
32943 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32944     
32945     files : false,
32946     
32947     indicator : 0,
32948     
32949     getAutoCreate : function()
32950     {
32951         var cfg = {
32952             tag : 'div',
32953             cls : 'roo-document-slider',
32954             cn : [
32955                 {
32956                     tag : 'div',
32957                     cls : 'roo-document-slider-header',
32958                     cn : [
32959                         {
32960                             tag : 'div',
32961                             cls : 'roo-document-slider-header-title'
32962                         }
32963                     ]
32964                 },
32965                 {
32966                     tag : 'div',
32967                     cls : 'roo-document-slider-body',
32968                     cn : [
32969                         {
32970                             tag : 'div',
32971                             cls : 'roo-document-slider-prev',
32972                             cn : [
32973                                 {
32974                                     tag : 'i',
32975                                     cls : 'fa fa-chevron-left'
32976                                 }
32977                             ]
32978                         },
32979                         {
32980                             tag : 'div',
32981                             cls : 'roo-document-slider-thumb',
32982                             cn : [
32983                                 {
32984                                     tag : 'img',
32985                                     cls : 'roo-document-slider-image'
32986                                 }
32987                             ]
32988                         },
32989                         {
32990                             tag : 'div',
32991                             cls : 'roo-document-slider-next',
32992                             cn : [
32993                                 {
32994                                     tag : 'i',
32995                                     cls : 'fa fa-chevron-right'
32996                                 }
32997                             ]
32998                         }
32999                     ]
33000                 }
33001             ]
33002         };
33003         
33004         return cfg;
33005     },
33006     
33007     initEvents : function()
33008     {
33009         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33010         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33011         
33012         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33013         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33014         
33015         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33016         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33017         
33018         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33019         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33020         
33021         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33022         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33023         
33024         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33025         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33026         
33027         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33028         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33029         
33030         this.thumbEl.on('click', this.onClick, this);
33031         
33032         this.prevIndicator.on('click', this.prev, this);
33033         
33034         this.nextIndicator.on('click', this.next, this);
33035         
33036     },
33037     
33038     initial : function()
33039     {
33040         if(this.files.length){
33041             this.indicator = 1;
33042             this.update()
33043         }
33044         
33045         this.fireEvent('initial', this);
33046     },
33047     
33048     update : function()
33049     {
33050         this.imageEl.attr('src', this.files[this.indicator - 1]);
33051         
33052         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33053         
33054         this.prevIndicator.show();
33055         
33056         if(this.indicator == 1){
33057             this.prevIndicator.hide();
33058         }
33059         
33060         this.nextIndicator.show();
33061         
33062         if(this.indicator == this.files.length){
33063             this.nextIndicator.hide();
33064         }
33065         
33066         this.thumbEl.scrollTo('top');
33067         
33068         this.fireEvent('update', this);
33069     },
33070     
33071     onClick : function(e)
33072     {
33073         e.preventDefault();
33074         
33075         this.fireEvent('click', this);
33076     },
33077     
33078     prev : function(e)
33079     {
33080         e.preventDefault();
33081         
33082         this.indicator = Math.max(1, this.indicator - 1);
33083         
33084         this.update();
33085     },
33086     
33087     next : function(e)
33088     {
33089         e.preventDefault();
33090         
33091         this.indicator = Math.min(this.files.length, this.indicator + 1);
33092         
33093         this.update();
33094     }
33095 });
33096 /*
33097  * - LGPL
33098  *
33099  * RadioSet
33100  *
33101  *
33102  */
33103
33104 /**
33105  * @class Roo.bootstrap.RadioSet
33106  * @extends Roo.bootstrap.Input
33107  * Bootstrap RadioSet class
33108  * @cfg {String} indicatorpos (left|right) default left
33109  * @cfg {Boolean} inline (true|false) inline the element (default true)
33110  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33111  * @constructor
33112  * Create a new RadioSet
33113  * @param {Object} config The config object
33114  */
33115
33116 Roo.bootstrap.RadioSet = function(config){
33117     
33118     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33119     
33120     this.radioes = [];
33121     
33122     Roo.bootstrap.RadioSet.register(this);
33123     
33124     this.addEvents({
33125         /**
33126         * @event check
33127         * Fires when the element is checked or unchecked.
33128         * @param {Roo.bootstrap.RadioSet} this This radio
33129         * @param {Roo.bootstrap.Radio} item The checked item
33130         */
33131        check : true
33132     });
33133     
33134 };
33135
33136 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33137
33138     radioes : false,
33139     
33140     inline : true,
33141     
33142     weight : '',
33143     
33144     indicatorpos : 'left',
33145     
33146     getAutoCreate : function()
33147     {
33148         var label = {
33149             tag : 'label',
33150             cls : 'roo-radio-set-label',
33151             cn : [
33152                 {
33153                     tag : 'span',
33154                     html : this.fieldLabel
33155                 }
33156             ]
33157         };
33158         
33159         if(this.indicatorpos == 'left'){
33160             label.cn.unshift({
33161                 tag : 'i',
33162                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33163                 tooltip : 'This field is required'
33164             });
33165         } else {
33166             label.cn.push({
33167                 tag : 'i',
33168                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33169                 tooltip : 'This field is required'
33170             });
33171         }
33172         
33173         var items = {
33174             tag : 'div',
33175             cls : 'roo-radio-set-items'
33176         };
33177         
33178         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33179         
33180         if (align === 'left' && this.fieldLabel.length) {
33181             
33182             items = {
33183                 cls : "roo-radio-set-right", 
33184                 cn: [
33185                     items
33186                 ]
33187             };
33188             
33189             if(this.labelWidth > 12){
33190                 label.style = "width: " + this.labelWidth + 'px';
33191             }
33192             
33193             if(this.labelWidth < 13 && this.labelmd == 0){
33194                 this.labelmd = this.labelWidth;
33195             }
33196             
33197             if(this.labellg > 0){
33198                 label.cls += ' col-lg-' + this.labellg;
33199                 items.cls += ' col-lg-' + (12 - this.labellg);
33200             }
33201             
33202             if(this.labelmd > 0){
33203                 label.cls += ' col-md-' + this.labelmd;
33204                 items.cls += ' col-md-' + (12 - this.labelmd);
33205             }
33206             
33207             if(this.labelsm > 0){
33208                 label.cls += ' col-sm-' + this.labelsm;
33209                 items.cls += ' col-sm-' + (12 - this.labelsm);
33210             }
33211             
33212             if(this.labelxs > 0){
33213                 label.cls += ' col-xs-' + this.labelxs;
33214                 items.cls += ' col-xs-' + (12 - this.labelxs);
33215             }
33216         }
33217         
33218         var cfg = {
33219             tag : 'div',
33220             cls : 'roo-radio-set',
33221             cn : [
33222                 {
33223                     tag : 'input',
33224                     cls : 'roo-radio-set-input',
33225                     type : 'hidden',
33226                     name : this.name,
33227                     value : this.value ? this.value :  ''
33228                 },
33229                 label,
33230                 items
33231             ]
33232         };
33233         
33234         if(this.weight.length){
33235             cfg.cls += ' roo-radio-' + this.weight;
33236         }
33237         
33238         if(this.inline) {
33239             cfg.cls += ' roo-radio-set-inline';
33240         }
33241         
33242         return cfg;
33243         
33244     },
33245
33246     initEvents : function()
33247     {
33248         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33249         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33250         
33251         if(!this.fieldLabel.length){
33252             this.labelEl.hide();
33253         }
33254         
33255         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33256         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33257         
33258         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33259         this.indicatorEl().hide();
33260         
33261         this.originalValue = this.getValue();
33262         
33263     },
33264     
33265     inputEl: function ()
33266     {
33267         return this.el.select('.roo-radio-set-input', true).first();
33268     },
33269     
33270     getChildContainer : function()
33271     {
33272         return this.itemsEl;
33273     },
33274     
33275     register : function(item)
33276     {
33277         this.radioes.push(item);
33278         
33279     },
33280     
33281     validate : function()
33282     {   
33283         var valid = false;
33284         
33285         Roo.each(this.radioes, function(i){
33286             if(!i.checked){
33287                 return;
33288             }
33289             
33290             valid = true;
33291             return false;
33292         });
33293         
33294         if(this.allowBlank) {
33295             return true;
33296         }
33297         
33298         if(this.disabled || valid){
33299             this.markValid();
33300             return true;
33301         }
33302         
33303         this.markInvalid();
33304         return false;
33305         
33306     },
33307     
33308     markValid : function()
33309     {
33310         if(this.labelEl.isVisible(true)){
33311             this.indicatorEl().hide();
33312         }
33313         
33314         this.el.removeClass([this.invalidClass, this.validClass]);
33315         this.el.addClass(this.validClass);
33316         
33317         this.fireEvent('valid', this);
33318     },
33319     
33320     markInvalid : function(msg)
33321     {
33322         if(this.allowBlank || this.disabled){
33323             return;
33324         }
33325         
33326         if(this.labelEl.isVisible(true)){
33327             this.indicatorEl().show();
33328         }
33329         
33330         this.el.removeClass([this.invalidClass, this.validClass]);
33331         this.el.addClass(this.invalidClass);
33332         
33333         this.fireEvent('invalid', this, msg);
33334         
33335     },
33336     
33337     setValue : function(v, suppressEvent)
33338     {   
33339         this.value = v;
33340         if(this.rendered){
33341             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33342         }
33343         
33344         Roo.each(this.radioes, function(i){
33345             
33346             i.checked = false;
33347             i.el.removeClass('checked');
33348             
33349             if(i.value === v || i.value.toString() === v.toString()){
33350                 i.checked = true;
33351                 i.el.addClass('checked');
33352                 
33353                 if(suppressEvent !== true){
33354                     this.fireEvent('check', this, i);
33355                 }
33356             }
33357             
33358         }, this);
33359         
33360         this.validate();
33361     },
33362     
33363     clearInvalid : function(){
33364         
33365         if(!this.el || this.preventMark){
33366             return;
33367         }
33368         
33369         this.el.removeClass([this.invalidClass]);
33370         
33371         this.fireEvent('valid', this);
33372     }
33373     
33374 });
33375
33376 Roo.apply(Roo.bootstrap.RadioSet, {
33377     
33378     groups: {},
33379     
33380     register : function(set)
33381     {
33382         this.groups[set.name] = set;
33383     },
33384     
33385     get: function(name) 
33386     {
33387         if (typeof(this.groups[name]) == 'undefined') {
33388             return false;
33389         }
33390         
33391         return this.groups[name] ;
33392     }
33393     
33394 });
33395 /*
33396  * Based on:
33397  * Ext JS Library 1.1.1
33398  * Copyright(c) 2006-2007, Ext JS, LLC.
33399  *
33400  * Originally Released Under LGPL - original licence link has changed is not relivant.
33401  *
33402  * Fork - LGPL
33403  * <script type="text/javascript">
33404  */
33405
33406
33407 /**
33408  * @class Roo.bootstrap.SplitBar
33409  * @extends Roo.util.Observable
33410  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33411  * <br><br>
33412  * Usage:
33413  * <pre><code>
33414 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33415                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33416 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33417 split.minSize = 100;
33418 split.maxSize = 600;
33419 split.animate = true;
33420 split.on('moved', splitterMoved);
33421 </code></pre>
33422  * @constructor
33423  * Create a new SplitBar
33424  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33425  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33426  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33427  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33428                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33429                         position of the SplitBar).
33430  */
33431 Roo.bootstrap.SplitBar = function(cfg){
33432     
33433     /** @private */
33434     
33435     //{
33436     //  dragElement : elm
33437     //  resizingElement: el,
33438         // optional..
33439     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33440     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33441         // existingProxy ???
33442     //}
33443     
33444     this.el = Roo.get(cfg.dragElement, true);
33445     this.el.dom.unselectable = "on";
33446     /** @private */
33447     this.resizingEl = Roo.get(cfg.resizingElement, true);
33448
33449     /**
33450      * @private
33451      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33452      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33453      * @type Number
33454      */
33455     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33456     
33457     /**
33458      * The minimum size of the resizing element. (Defaults to 0)
33459      * @type Number
33460      */
33461     this.minSize = 0;
33462     
33463     /**
33464      * The maximum size of the resizing element. (Defaults to 2000)
33465      * @type Number
33466      */
33467     this.maxSize = 2000;
33468     
33469     /**
33470      * Whether to animate the transition to the new size
33471      * @type Boolean
33472      */
33473     this.animate = false;
33474     
33475     /**
33476      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33477      * @type Boolean
33478      */
33479     this.useShim = false;
33480     
33481     /** @private */
33482     this.shim = null;
33483     
33484     if(!cfg.existingProxy){
33485         /** @private */
33486         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33487     }else{
33488         this.proxy = Roo.get(cfg.existingProxy).dom;
33489     }
33490     /** @private */
33491     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33492     
33493     /** @private */
33494     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33495     
33496     /** @private */
33497     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33498     
33499     /** @private */
33500     this.dragSpecs = {};
33501     
33502     /**
33503      * @private The adapter to use to positon and resize elements
33504      */
33505     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33506     this.adapter.init(this);
33507     
33508     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33509         /** @private */
33510         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33511         this.el.addClass("roo-splitbar-h");
33512     }else{
33513         /** @private */
33514         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33515         this.el.addClass("roo-splitbar-v");
33516     }
33517     
33518     this.addEvents({
33519         /**
33520          * @event resize
33521          * Fires when the splitter is moved (alias for {@link #event-moved})
33522          * @param {Roo.bootstrap.SplitBar} this
33523          * @param {Number} newSize the new width or height
33524          */
33525         "resize" : true,
33526         /**
33527          * @event moved
33528          * Fires when the splitter is moved
33529          * @param {Roo.bootstrap.SplitBar} this
33530          * @param {Number} newSize the new width or height
33531          */
33532         "moved" : true,
33533         /**
33534          * @event beforeresize
33535          * Fires before the splitter is dragged
33536          * @param {Roo.bootstrap.SplitBar} this
33537          */
33538         "beforeresize" : true,
33539
33540         "beforeapply" : true
33541     });
33542
33543     Roo.util.Observable.call(this);
33544 };
33545
33546 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33547     onStartProxyDrag : function(x, y){
33548         this.fireEvent("beforeresize", this);
33549         if(!this.overlay){
33550             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33551             o.unselectable();
33552             o.enableDisplayMode("block");
33553             // all splitbars share the same overlay
33554             Roo.bootstrap.SplitBar.prototype.overlay = o;
33555         }
33556         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33557         this.overlay.show();
33558         Roo.get(this.proxy).setDisplayed("block");
33559         var size = this.adapter.getElementSize(this);
33560         this.activeMinSize = this.getMinimumSize();;
33561         this.activeMaxSize = this.getMaximumSize();;
33562         var c1 = size - this.activeMinSize;
33563         var c2 = Math.max(this.activeMaxSize - size, 0);
33564         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33565             this.dd.resetConstraints();
33566             this.dd.setXConstraint(
33567                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33568                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33569             );
33570             this.dd.setYConstraint(0, 0);
33571         }else{
33572             this.dd.resetConstraints();
33573             this.dd.setXConstraint(0, 0);
33574             this.dd.setYConstraint(
33575                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33576                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33577             );
33578          }
33579         this.dragSpecs.startSize = size;
33580         this.dragSpecs.startPoint = [x, y];
33581         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33582     },
33583     
33584     /** 
33585      * @private Called after the drag operation by the DDProxy
33586      */
33587     onEndProxyDrag : function(e){
33588         Roo.get(this.proxy).setDisplayed(false);
33589         var endPoint = Roo.lib.Event.getXY(e);
33590         if(this.overlay){
33591             this.overlay.hide();
33592         }
33593         var newSize;
33594         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33595             newSize = this.dragSpecs.startSize + 
33596                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33597                     endPoint[0] - this.dragSpecs.startPoint[0] :
33598                     this.dragSpecs.startPoint[0] - endPoint[0]
33599                 );
33600         }else{
33601             newSize = this.dragSpecs.startSize + 
33602                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33603                     endPoint[1] - this.dragSpecs.startPoint[1] :
33604                     this.dragSpecs.startPoint[1] - endPoint[1]
33605                 );
33606         }
33607         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33608         if(newSize != this.dragSpecs.startSize){
33609             if(this.fireEvent('beforeapply', this, newSize) !== false){
33610                 this.adapter.setElementSize(this, newSize);
33611                 this.fireEvent("moved", this, newSize);
33612                 this.fireEvent("resize", this, newSize);
33613             }
33614         }
33615     },
33616     
33617     /**
33618      * Get the adapter this SplitBar uses
33619      * @return The adapter object
33620      */
33621     getAdapter : function(){
33622         return this.adapter;
33623     },
33624     
33625     /**
33626      * Set the adapter this SplitBar uses
33627      * @param {Object} adapter A SplitBar adapter object
33628      */
33629     setAdapter : function(adapter){
33630         this.adapter = adapter;
33631         this.adapter.init(this);
33632     },
33633     
33634     /**
33635      * Gets the minimum size for the resizing element
33636      * @return {Number} The minimum size
33637      */
33638     getMinimumSize : function(){
33639         return this.minSize;
33640     },
33641     
33642     /**
33643      * Sets the minimum size for the resizing element
33644      * @param {Number} minSize The minimum size
33645      */
33646     setMinimumSize : function(minSize){
33647         this.minSize = minSize;
33648     },
33649     
33650     /**
33651      * Gets the maximum size for the resizing element
33652      * @return {Number} The maximum size
33653      */
33654     getMaximumSize : function(){
33655         return this.maxSize;
33656     },
33657     
33658     /**
33659      * Sets the maximum size for the resizing element
33660      * @param {Number} maxSize The maximum size
33661      */
33662     setMaximumSize : function(maxSize){
33663         this.maxSize = maxSize;
33664     },
33665     
33666     /**
33667      * Sets the initialize size for the resizing element
33668      * @param {Number} size The initial size
33669      */
33670     setCurrentSize : function(size){
33671         var oldAnimate = this.animate;
33672         this.animate = false;
33673         this.adapter.setElementSize(this, size);
33674         this.animate = oldAnimate;
33675     },
33676     
33677     /**
33678      * Destroy this splitbar. 
33679      * @param {Boolean} removeEl True to remove the element
33680      */
33681     destroy : function(removeEl){
33682         if(this.shim){
33683             this.shim.remove();
33684         }
33685         this.dd.unreg();
33686         this.proxy.parentNode.removeChild(this.proxy);
33687         if(removeEl){
33688             this.el.remove();
33689         }
33690     }
33691 });
33692
33693 /**
33694  * @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.
33695  */
33696 Roo.bootstrap.SplitBar.createProxy = function(dir){
33697     var proxy = new Roo.Element(document.createElement("div"));
33698     proxy.unselectable();
33699     var cls = 'roo-splitbar-proxy';
33700     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33701     document.body.appendChild(proxy.dom);
33702     return proxy.dom;
33703 };
33704
33705 /** 
33706  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33707  * Default Adapter. It assumes the splitter and resizing element are not positioned
33708  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33709  */
33710 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33711 };
33712
33713 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33714     // do nothing for now
33715     init : function(s){
33716     
33717     },
33718     /**
33719      * Called before drag operations to get the current size of the resizing element. 
33720      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33721      */
33722      getElementSize : function(s){
33723         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33724             return s.resizingEl.getWidth();
33725         }else{
33726             return s.resizingEl.getHeight();
33727         }
33728     },
33729     
33730     /**
33731      * Called after drag operations to set the size of the resizing element.
33732      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33733      * @param {Number} newSize The new size to set
33734      * @param {Function} onComplete A function to be invoked when resizing is complete
33735      */
33736     setElementSize : function(s, newSize, onComplete){
33737         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33738             if(!s.animate){
33739                 s.resizingEl.setWidth(newSize);
33740                 if(onComplete){
33741                     onComplete(s, newSize);
33742                 }
33743             }else{
33744                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33745             }
33746         }else{
33747             
33748             if(!s.animate){
33749                 s.resizingEl.setHeight(newSize);
33750                 if(onComplete){
33751                     onComplete(s, newSize);
33752                 }
33753             }else{
33754                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33755             }
33756         }
33757     }
33758 };
33759
33760 /** 
33761  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33762  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33763  * Adapter that  moves the splitter element to align with the resized sizing element. 
33764  * Used with an absolute positioned SplitBar.
33765  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33766  * document.body, make sure you assign an id to the body element.
33767  */
33768 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33769     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33770     this.container = Roo.get(container);
33771 };
33772
33773 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33774     init : function(s){
33775         this.basic.init(s);
33776     },
33777     
33778     getElementSize : function(s){
33779         return this.basic.getElementSize(s);
33780     },
33781     
33782     setElementSize : function(s, newSize, onComplete){
33783         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33784     },
33785     
33786     moveSplitter : function(s){
33787         var yes = Roo.bootstrap.SplitBar;
33788         switch(s.placement){
33789             case yes.LEFT:
33790                 s.el.setX(s.resizingEl.getRight());
33791                 break;
33792             case yes.RIGHT:
33793                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33794                 break;
33795             case yes.TOP:
33796                 s.el.setY(s.resizingEl.getBottom());
33797                 break;
33798             case yes.BOTTOM:
33799                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33800                 break;
33801         }
33802     }
33803 };
33804
33805 /**
33806  * Orientation constant - Create a vertical SplitBar
33807  * @static
33808  * @type Number
33809  */
33810 Roo.bootstrap.SplitBar.VERTICAL = 1;
33811
33812 /**
33813  * Orientation constant - Create a horizontal SplitBar
33814  * @static
33815  * @type Number
33816  */
33817 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33818
33819 /**
33820  * Placement constant - The resizing element is to the left of the splitter element
33821  * @static
33822  * @type Number
33823  */
33824 Roo.bootstrap.SplitBar.LEFT = 1;
33825
33826 /**
33827  * Placement constant - The resizing element is to the right of the splitter element
33828  * @static
33829  * @type Number
33830  */
33831 Roo.bootstrap.SplitBar.RIGHT = 2;
33832
33833 /**
33834  * Placement constant - The resizing element is positioned above the splitter element
33835  * @static
33836  * @type Number
33837  */
33838 Roo.bootstrap.SplitBar.TOP = 3;
33839
33840 /**
33841  * Placement constant - The resizing element is positioned under splitter element
33842  * @static
33843  * @type Number
33844  */
33845 Roo.bootstrap.SplitBar.BOTTOM = 4;
33846 Roo.namespace("Roo.bootstrap.layout");/*
33847  * Based on:
33848  * Ext JS Library 1.1.1
33849  * Copyright(c) 2006-2007, Ext JS, LLC.
33850  *
33851  * Originally Released Under LGPL - original licence link has changed is not relivant.
33852  *
33853  * Fork - LGPL
33854  * <script type="text/javascript">
33855  */
33856
33857 /**
33858  * @class Roo.bootstrap.layout.Manager
33859  * @extends Roo.bootstrap.Component
33860  * Base class for layout managers.
33861  */
33862 Roo.bootstrap.layout.Manager = function(config)
33863 {
33864     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33865
33866
33867
33868
33869
33870     /** false to disable window resize monitoring @type Boolean */
33871     this.monitorWindowResize = true;
33872     this.regions = {};
33873     this.addEvents({
33874         /**
33875          * @event layout
33876          * Fires when a layout is performed.
33877          * @param {Roo.LayoutManager} this
33878          */
33879         "layout" : true,
33880         /**
33881          * @event regionresized
33882          * Fires when the user resizes a region.
33883          * @param {Roo.LayoutRegion} region The resized region
33884          * @param {Number} newSize The new size (width for east/west, height for north/south)
33885          */
33886         "regionresized" : true,
33887         /**
33888          * @event regioncollapsed
33889          * Fires when a region is collapsed.
33890          * @param {Roo.LayoutRegion} region The collapsed region
33891          */
33892         "regioncollapsed" : true,
33893         /**
33894          * @event regionexpanded
33895          * Fires when a region is expanded.
33896          * @param {Roo.LayoutRegion} region The expanded region
33897          */
33898         "regionexpanded" : true
33899     });
33900     this.updating = false;
33901
33902     if (config.el) {
33903         this.el = Roo.get(config.el);
33904         this.initEvents();
33905     }
33906
33907 };
33908
33909 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33910
33911
33912     regions : null,
33913
33914     monitorWindowResize : true,
33915
33916
33917     updating : false,
33918
33919
33920     onRender : function(ct, position)
33921     {
33922         if(!this.el){
33923             this.el = Roo.get(ct);
33924             this.initEvents();
33925         }
33926         //this.fireEvent('render',this);
33927     },
33928
33929
33930     initEvents: function()
33931     {
33932
33933
33934         // ie scrollbar fix
33935         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33936             document.body.scroll = "no";
33937         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33938             this.el.position('relative');
33939         }
33940         this.id = this.el.id;
33941         this.el.addClass("roo-layout-container");
33942         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33943         if(this.el.dom != document.body ) {
33944             this.el.on('resize', this.layout,this);
33945             this.el.on('show', this.layout,this);
33946         }
33947
33948     },
33949
33950     /**
33951      * Returns true if this layout is currently being updated
33952      * @return {Boolean}
33953      */
33954     isUpdating : function(){
33955         return this.updating;
33956     },
33957
33958     /**
33959      * Suspend the LayoutManager from doing auto-layouts while
33960      * making multiple add or remove calls
33961      */
33962     beginUpdate : function(){
33963         this.updating = true;
33964     },
33965
33966     /**
33967      * Restore auto-layouts and optionally disable the manager from performing a layout
33968      * @param {Boolean} noLayout true to disable a layout update
33969      */
33970     endUpdate : function(noLayout){
33971         this.updating = false;
33972         if(!noLayout){
33973             this.layout();
33974         }
33975     },
33976
33977     layout: function(){
33978         // abstract...
33979     },
33980
33981     onRegionResized : function(region, newSize){
33982         this.fireEvent("regionresized", region, newSize);
33983         this.layout();
33984     },
33985
33986     onRegionCollapsed : function(region){
33987         this.fireEvent("regioncollapsed", region);
33988     },
33989
33990     onRegionExpanded : function(region){
33991         this.fireEvent("regionexpanded", region);
33992     },
33993
33994     /**
33995      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33996      * performs box-model adjustments.
33997      * @return {Object} The size as an object {width: (the width), height: (the height)}
33998      */
33999     getViewSize : function()
34000     {
34001         var size;
34002         if(this.el.dom != document.body){
34003             size = this.el.getSize();
34004         }else{
34005             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34006         }
34007         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34008         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34009         return size;
34010     },
34011
34012     /**
34013      * Returns the Element this layout is bound to.
34014      * @return {Roo.Element}
34015      */
34016     getEl : function(){
34017         return this.el;
34018     },
34019
34020     /**
34021      * Returns the specified region.
34022      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34023      * @return {Roo.LayoutRegion}
34024      */
34025     getRegion : function(target){
34026         return this.regions[target.toLowerCase()];
34027     },
34028
34029     onWindowResize : function(){
34030         if(this.monitorWindowResize){
34031             this.layout();
34032         }
34033     }
34034 });
34035 /*
34036  * Based on:
34037  * Ext JS Library 1.1.1
34038  * Copyright(c) 2006-2007, Ext JS, LLC.
34039  *
34040  * Originally Released Under LGPL - original licence link has changed is not relivant.
34041  *
34042  * Fork - LGPL
34043  * <script type="text/javascript">
34044  */
34045 /**
34046  * @class Roo.bootstrap.layout.Border
34047  * @extends Roo.bootstrap.layout.Manager
34048  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34049  * please see: examples/bootstrap/nested.html<br><br>
34050  
34051 <b>The container the layout is rendered into can be either the body element or any other element.
34052 If it is not the body element, the container needs to either be an absolute positioned element,
34053 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34054 the container size if it is not the body element.</b>
34055
34056 * @constructor
34057 * Create a new Border
34058 * @param {Object} config Configuration options
34059  */
34060 Roo.bootstrap.layout.Border = function(config){
34061     config = config || {};
34062     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34063     
34064     
34065     
34066     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34067         if(config[region]){
34068             config[region].region = region;
34069             this.addRegion(config[region]);
34070         }
34071     },this);
34072     
34073 };
34074
34075 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34076
34077 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34078     /**
34079      * Creates and adds a new region if it doesn't already exist.
34080      * @param {String} target The target region key (north, south, east, west or center).
34081      * @param {Object} config The regions config object
34082      * @return {BorderLayoutRegion} The new region
34083      */
34084     addRegion : function(config)
34085     {
34086         if(!this.regions[config.region]){
34087             var r = this.factory(config);
34088             this.bindRegion(r);
34089         }
34090         return this.regions[config.region];
34091     },
34092
34093     // private (kinda)
34094     bindRegion : function(r){
34095         this.regions[r.config.region] = r;
34096         
34097         r.on("visibilitychange",    this.layout, this);
34098         r.on("paneladded",          this.layout, this);
34099         r.on("panelremoved",        this.layout, this);
34100         r.on("invalidated",         this.layout, this);
34101         r.on("resized",             this.onRegionResized, this);
34102         r.on("collapsed",           this.onRegionCollapsed, this);
34103         r.on("expanded",            this.onRegionExpanded, this);
34104     },
34105
34106     /**
34107      * Performs a layout update.
34108      */
34109     layout : function()
34110     {
34111         if(this.updating) {
34112             return;
34113         }
34114         
34115         // render all the rebions if they have not been done alreayd?
34116         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34117             if(this.regions[region] && !this.regions[region].bodyEl){
34118                 this.regions[region].onRender(this.el)
34119             }
34120         },this);
34121         
34122         var size = this.getViewSize();
34123         var w = size.width;
34124         var h = size.height;
34125         var centerW = w;
34126         var centerH = h;
34127         var centerY = 0;
34128         var centerX = 0;
34129         //var x = 0, y = 0;
34130
34131         var rs = this.regions;
34132         var north = rs["north"];
34133         var south = rs["south"]; 
34134         var west = rs["west"];
34135         var east = rs["east"];
34136         var center = rs["center"];
34137         //if(this.hideOnLayout){ // not supported anymore
34138             //c.el.setStyle("display", "none");
34139         //}
34140         if(north && north.isVisible()){
34141             var b = north.getBox();
34142             var m = north.getMargins();
34143             b.width = w - (m.left+m.right);
34144             b.x = m.left;
34145             b.y = m.top;
34146             centerY = b.height + b.y + m.bottom;
34147             centerH -= centerY;
34148             north.updateBox(this.safeBox(b));
34149         }
34150         if(south && south.isVisible()){
34151             var b = south.getBox();
34152             var m = south.getMargins();
34153             b.width = w - (m.left+m.right);
34154             b.x = m.left;
34155             var totalHeight = (b.height + m.top + m.bottom);
34156             b.y = h - totalHeight + m.top;
34157             centerH -= totalHeight;
34158             south.updateBox(this.safeBox(b));
34159         }
34160         if(west && west.isVisible()){
34161             var b = west.getBox();
34162             var m = west.getMargins();
34163             b.height = centerH - (m.top+m.bottom);
34164             b.x = m.left;
34165             b.y = centerY + m.top;
34166             var totalWidth = (b.width + m.left + m.right);
34167             centerX += totalWidth;
34168             centerW -= totalWidth;
34169             west.updateBox(this.safeBox(b));
34170         }
34171         if(east && east.isVisible()){
34172             var b = east.getBox();
34173             var m = east.getMargins();
34174             b.height = centerH - (m.top+m.bottom);
34175             var totalWidth = (b.width + m.left + m.right);
34176             b.x = w - totalWidth + m.left;
34177             b.y = centerY + m.top;
34178             centerW -= totalWidth;
34179             east.updateBox(this.safeBox(b));
34180         }
34181         if(center){
34182             var m = center.getMargins();
34183             var centerBox = {
34184                 x: centerX + m.left,
34185                 y: centerY + m.top,
34186                 width: centerW - (m.left+m.right),
34187                 height: centerH - (m.top+m.bottom)
34188             };
34189             //if(this.hideOnLayout){
34190                 //center.el.setStyle("display", "block");
34191             //}
34192             center.updateBox(this.safeBox(centerBox));
34193         }
34194         this.el.repaint();
34195         this.fireEvent("layout", this);
34196     },
34197
34198     // private
34199     safeBox : function(box){
34200         box.width = Math.max(0, box.width);
34201         box.height = Math.max(0, box.height);
34202         return box;
34203     },
34204
34205     /**
34206      * Adds a ContentPanel (or subclass) to this layout.
34207      * @param {String} target The target region key (north, south, east, west or center).
34208      * @param {Roo.ContentPanel} panel The panel to add
34209      * @return {Roo.ContentPanel} The added panel
34210      */
34211     add : function(target, panel){
34212          
34213         target = target.toLowerCase();
34214         return this.regions[target].add(panel);
34215     },
34216
34217     /**
34218      * Remove a ContentPanel (or subclass) to this layout.
34219      * @param {String} target The target region key (north, south, east, west or center).
34220      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34221      * @return {Roo.ContentPanel} The removed panel
34222      */
34223     remove : function(target, panel){
34224         target = target.toLowerCase();
34225         return this.regions[target].remove(panel);
34226     },
34227
34228     /**
34229      * Searches all regions for a panel with the specified id
34230      * @param {String} panelId
34231      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34232      */
34233     findPanel : function(panelId){
34234         var rs = this.regions;
34235         for(var target in rs){
34236             if(typeof rs[target] != "function"){
34237                 var p = rs[target].getPanel(panelId);
34238                 if(p){
34239                     return p;
34240                 }
34241             }
34242         }
34243         return null;
34244     },
34245
34246     /**
34247      * Searches all regions for a panel with the specified id and activates (shows) it.
34248      * @param {String/ContentPanel} panelId The panels id or the panel itself
34249      * @return {Roo.ContentPanel} The shown panel or null
34250      */
34251     showPanel : function(panelId) {
34252       var rs = this.regions;
34253       for(var target in rs){
34254          var r = rs[target];
34255          if(typeof r != "function"){
34256             if(r.hasPanel(panelId)){
34257                return r.showPanel(panelId);
34258             }
34259          }
34260       }
34261       return null;
34262    },
34263
34264    /**
34265      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34266      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34267      */
34268    /*
34269     restoreState : function(provider){
34270         if(!provider){
34271             provider = Roo.state.Manager;
34272         }
34273         var sm = new Roo.LayoutStateManager();
34274         sm.init(this, provider);
34275     },
34276 */
34277  
34278  
34279     /**
34280      * Adds a xtype elements to the layout.
34281      * <pre><code>
34282
34283 layout.addxtype({
34284        xtype : 'ContentPanel',
34285        region: 'west',
34286        items: [ .... ]
34287    }
34288 );
34289
34290 layout.addxtype({
34291         xtype : 'NestedLayoutPanel',
34292         region: 'west',
34293         layout: {
34294            center: { },
34295            west: { }   
34296         },
34297         items : [ ... list of content panels or nested layout panels.. ]
34298    }
34299 );
34300 </code></pre>
34301      * @param {Object} cfg Xtype definition of item to add.
34302      */
34303     addxtype : function(cfg)
34304     {
34305         // basically accepts a pannel...
34306         // can accept a layout region..!?!?
34307         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34308         
34309         
34310         // theory?  children can only be panels??
34311         
34312         //if (!cfg.xtype.match(/Panel$/)) {
34313         //    return false;
34314         //}
34315         var ret = false;
34316         
34317         if (typeof(cfg.region) == 'undefined') {
34318             Roo.log("Failed to add Panel, region was not set");
34319             Roo.log(cfg);
34320             return false;
34321         }
34322         var region = cfg.region;
34323         delete cfg.region;
34324         
34325           
34326         var xitems = [];
34327         if (cfg.items) {
34328             xitems = cfg.items;
34329             delete cfg.items;
34330         }
34331         var nb = false;
34332         
34333         switch(cfg.xtype) 
34334         {
34335             case 'Content':  // ContentPanel (el, cfg)
34336             case 'Scroll':  // ContentPanel (el, cfg)
34337             case 'View': 
34338                 cfg.autoCreate = true;
34339                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34340                 //} else {
34341                 //    var el = this.el.createChild();
34342                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34343                 //}
34344                 
34345                 this.add(region, ret);
34346                 break;
34347             
34348             /*
34349             case 'TreePanel': // our new panel!
34350                 cfg.el = this.el.createChild();
34351                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34352                 this.add(region, ret);
34353                 break;
34354             */
34355             
34356             case 'Nest': 
34357                 // create a new Layout (which is  a Border Layout...
34358                 
34359                 var clayout = cfg.layout;
34360                 clayout.el  = this.el.createChild();
34361                 clayout.items   = clayout.items  || [];
34362                 
34363                 delete cfg.layout;
34364                 
34365                 // replace this exitems with the clayout ones..
34366                 xitems = clayout.items;
34367                  
34368                 // force background off if it's in center...
34369                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34370                     cfg.background = false;
34371                 }
34372                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34373                 
34374                 
34375                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34376                 //console.log('adding nested layout panel '  + cfg.toSource());
34377                 this.add(region, ret);
34378                 nb = {}; /// find first...
34379                 break;
34380             
34381             case 'Grid':
34382                 
34383                 // needs grid and region
34384                 
34385                 //var el = this.getRegion(region).el.createChild();
34386                 /*
34387                  *var el = this.el.createChild();
34388                 // create the grid first...
34389                 cfg.grid.container = el;
34390                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34391                 */
34392                 
34393                 if (region == 'center' && this.active ) {
34394                     cfg.background = false;
34395                 }
34396                 
34397                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34398                 
34399                 this.add(region, ret);
34400                 /*
34401                 if (cfg.background) {
34402                     // render grid on panel activation (if panel background)
34403                     ret.on('activate', function(gp) {
34404                         if (!gp.grid.rendered) {
34405                     //        gp.grid.render(el);
34406                         }
34407                     });
34408                 } else {
34409                   //  cfg.grid.render(el);
34410                 }
34411                 */
34412                 break;
34413            
34414            
34415             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34416                 // it was the old xcomponent building that caused this before.
34417                 // espeically if border is the top element in the tree.
34418                 ret = this;
34419                 break; 
34420                 
34421                     
34422                 
34423                 
34424                 
34425             default:
34426                 /*
34427                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34428                     
34429                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34430                     this.add(region, ret);
34431                 } else {
34432                 */
34433                     Roo.log(cfg);
34434                     throw "Can not add '" + cfg.xtype + "' to Border";
34435                     return null;
34436              
34437                                 
34438              
34439         }
34440         this.beginUpdate();
34441         // add children..
34442         var region = '';
34443         var abn = {};
34444         Roo.each(xitems, function(i)  {
34445             region = nb && i.region ? i.region : false;
34446             
34447             var add = ret.addxtype(i);
34448            
34449             if (region) {
34450                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34451                 if (!i.background) {
34452                     abn[region] = nb[region] ;
34453                 }
34454             }
34455             
34456         });
34457         this.endUpdate();
34458
34459         // make the last non-background panel active..
34460         //if (nb) { Roo.log(abn); }
34461         if (nb) {
34462             
34463             for(var r in abn) {
34464                 region = this.getRegion(r);
34465                 if (region) {
34466                     // tried using nb[r], but it does not work..
34467                      
34468                     region.showPanel(abn[r]);
34469                    
34470                 }
34471             }
34472         }
34473         return ret;
34474         
34475     },
34476     
34477     
34478 // private
34479     factory : function(cfg)
34480     {
34481         
34482         var validRegions = Roo.bootstrap.layout.Border.regions;
34483
34484         var target = cfg.region;
34485         cfg.mgr = this;
34486         
34487         var r = Roo.bootstrap.layout;
34488         Roo.log(target);
34489         switch(target){
34490             case "north":
34491                 return new r.North(cfg);
34492             case "south":
34493                 return new r.South(cfg);
34494             case "east":
34495                 return new r.East(cfg);
34496             case "west":
34497                 return new r.West(cfg);
34498             case "center":
34499                 return new r.Center(cfg);
34500         }
34501         throw 'Layout region "'+target+'" not supported.';
34502     }
34503     
34504     
34505 });
34506  /*
34507  * Based on:
34508  * Ext JS Library 1.1.1
34509  * Copyright(c) 2006-2007, Ext JS, LLC.
34510  *
34511  * Originally Released Under LGPL - original licence link has changed is not relivant.
34512  *
34513  * Fork - LGPL
34514  * <script type="text/javascript">
34515  */
34516  
34517 /**
34518  * @class Roo.bootstrap.layout.Basic
34519  * @extends Roo.util.Observable
34520  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34521  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34522  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34523  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34524  * @cfg {string}   region  the region that it inhabits..
34525  * @cfg {bool}   skipConfig skip config?
34526  * 
34527
34528  */
34529 Roo.bootstrap.layout.Basic = function(config){
34530     
34531     this.mgr = config.mgr;
34532     
34533     this.position = config.region;
34534     
34535     var skipConfig = config.skipConfig;
34536     
34537     this.events = {
34538         /**
34539          * @scope Roo.BasicLayoutRegion
34540          */
34541         
34542         /**
34543          * @event beforeremove
34544          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34545          * @param {Roo.LayoutRegion} this
34546          * @param {Roo.ContentPanel} panel The panel
34547          * @param {Object} e The cancel event object
34548          */
34549         "beforeremove" : true,
34550         /**
34551          * @event invalidated
34552          * Fires when the layout for this region is changed.
34553          * @param {Roo.LayoutRegion} this
34554          */
34555         "invalidated" : true,
34556         /**
34557          * @event visibilitychange
34558          * Fires when this region is shown or hidden 
34559          * @param {Roo.LayoutRegion} this
34560          * @param {Boolean} visibility true or false
34561          */
34562         "visibilitychange" : true,
34563         /**
34564          * @event paneladded
34565          * Fires when a panel is added. 
34566          * @param {Roo.LayoutRegion} this
34567          * @param {Roo.ContentPanel} panel The panel
34568          */
34569         "paneladded" : true,
34570         /**
34571          * @event panelremoved
34572          * Fires when a panel is removed. 
34573          * @param {Roo.LayoutRegion} this
34574          * @param {Roo.ContentPanel} panel The panel
34575          */
34576         "panelremoved" : true,
34577         /**
34578          * @event beforecollapse
34579          * Fires when this region before collapse.
34580          * @param {Roo.LayoutRegion} this
34581          */
34582         "beforecollapse" : true,
34583         /**
34584          * @event collapsed
34585          * Fires when this region is collapsed.
34586          * @param {Roo.LayoutRegion} this
34587          */
34588         "collapsed" : true,
34589         /**
34590          * @event expanded
34591          * Fires when this region is expanded.
34592          * @param {Roo.LayoutRegion} this
34593          */
34594         "expanded" : true,
34595         /**
34596          * @event slideshow
34597          * Fires when this region is slid into view.
34598          * @param {Roo.LayoutRegion} this
34599          */
34600         "slideshow" : true,
34601         /**
34602          * @event slidehide
34603          * Fires when this region slides out of view. 
34604          * @param {Roo.LayoutRegion} this
34605          */
34606         "slidehide" : true,
34607         /**
34608          * @event panelactivated
34609          * Fires when a panel is activated. 
34610          * @param {Roo.LayoutRegion} this
34611          * @param {Roo.ContentPanel} panel The activated panel
34612          */
34613         "panelactivated" : true,
34614         /**
34615          * @event resized
34616          * Fires when the user resizes this region. 
34617          * @param {Roo.LayoutRegion} this
34618          * @param {Number} newSize The new size (width for east/west, height for north/south)
34619          */
34620         "resized" : true
34621     };
34622     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34623     this.panels = new Roo.util.MixedCollection();
34624     this.panels.getKey = this.getPanelId.createDelegate(this);
34625     this.box = null;
34626     this.activePanel = null;
34627     // ensure listeners are added...
34628     
34629     if (config.listeners || config.events) {
34630         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34631             listeners : config.listeners || {},
34632             events : config.events || {}
34633         });
34634     }
34635     
34636     if(skipConfig !== true){
34637         this.applyConfig(config);
34638     }
34639 };
34640
34641 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34642 {
34643     getPanelId : function(p){
34644         return p.getId();
34645     },
34646     
34647     applyConfig : function(config){
34648         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34649         this.config = config;
34650         
34651     },
34652     
34653     /**
34654      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34655      * the width, for horizontal (north, south) the height.
34656      * @param {Number} newSize The new width or height
34657      */
34658     resizeTo : function(newSize){
34659         var el = this.el ? this.el :
34660                  (this.activePanel ? this.activePanel.getEl() : null);
34661         if(el){
34662             switch(this.position){
34663                 case "east":
34664                 case "west":
34665                     el.setWidth(newSize);
34666                     this.fireEvent("resized", this, newSize);
34667                 break;
34668                 case "north":
34669                 case "south":
34670                     el.setHeight(newSize);
34671                     this.fireEvent("resized", this, newSize);
34672                 break;                
34673             }
34674         }
34675     },
34676     
34677     getBox : function(){
34678         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34679     },
34680     
34681     getMargins : function(){
34682         return this.margins;
34683     },
34684     
34685     updateBox : function(box){
34686         this.box = box;
34687         var el = this.activePanel.getEl();
34688         el.dom.style.left = box.x + "px";
34689         el.dom.style.top = box.y + "px";
34690         this.activePanel.setSize(box.width, box.height);
34691     },
34692     
34693     /**
34694      * Returns the container element for this region.
34695      * @return {Roo.Element}
34696      */
34697     getEl : function(){
34698         return this.activePanel;
34699     },
34700     
34701     /**
34702      * Returns true if this region is currently visible.
34703      * @return {Boolean}
34704      */
34705     isVisible : function(){
34706         return this.activePanel ? true : false;
34707     },
34708     
34709     setActivePanel : function(panel){
34710         panel = this.getPanel(panel);
34711         if(this.activePanel && this.activePanel != panel){
34712             this.activePanel.setActiveState(false);
34713             this.activePanel.getEl().setLeftTop(-10000,-10000);
34714         }
34715         this.activePanel = panel;
34716         panel.setActiveState(true);
34717         if(this.box){
34718             panel.setSize(this.box.width, this.box.height);
34719         }
34720         this.fireEvent("panelactivated", this, panel);
34721         this.fireEvent("invalidated");
34722     },
34723     
34724     /**
34725      * Show the specified panel.
34726      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34727      * @return {Roo.ContentPanel} The shown panel or null
34728      */
34729     showPanel : function(panel){
34730         panel = this.getPanel(panel);
34731         if(panel){
34732             this.setActivePanel(panel);
34733         }
34734         return panel;
34735     },
34736     
34737     /**
34738      * Get the active panel for this region.
34739      * @return {Roo.ContentPanel} The active panel or null
34740      */
34741     getActivePanel : function(){
34742         return this.activePanel;
34743     },
34744     
34745     /**
34746      * Add the passed ContentPanel(s)
34747      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34748      * @return {Roo.ContentPanel} The panel added (if only one was added)
34749      */
34750     add : function(panel){
34751         if(arguments.length > 1){
34752             for(var i = 0, len = arguments.length; i < len; i++) {
34753                 this.add(arguments[i]);
34754             }
34755             return null;
34756         }
34757         if(this.hasPanel(panel)){
34758             this.showPanel(panel);
34759             return panel;
34760         }
34761         var el = panel.getEl();
34762         if(el.dom.parentNode != this.mgr.el.dom){
34763             this.mgr.el.dom.appendChild(el.dom);
34764         }
34765         if(panel.setRegion){
34766             panel.setRegion(this);
34767         }
34768         this.panels.add(panel);
34769         el.setStyle("position", "absolute");
34770         if(!panel.background){
34771             this.setActivePanel(panel);
34772             if(this.config.initialSize && this.panels.getCount()==1){
34773                 this.resizeTo(this.config.initialSize);
34774             }
34775         }
34776         this.fireEvent("paneladded", this, panel);
34777         return panel;
34778     },
34779     
34780     /**
34781      * Returns true if the panel is in this region.
34782      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34783      * @return {Boolean}
34784      */
34785     hasPanel : function(panel){
34786         if(typeof panel == "object"){ // must be panel obj
34787             panel = panel.getId();
34788         }
34789         return this.getPanel(panel) ? true : false;
34790     },
34791     
34792     /**
34793      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34794      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34795      * @param {Boolean} preservePanel Overrides the config preservePanel option
34796      * @return {Roo.ContentPanel} The panel that was removed
34797      */
34798     remove : function(panel, preservePanel){
34799         panel = this.getPanel(panel);
34800         if(!panel){
34801             return null;
34802         }
34803         var e = {};
34804         this.fireEvent("beforeremove", this, panel, e);
34805         if(e.cancel === true){
34806             return null;
34807         }
34808         var panelId = panel.getId();
34809         this.panels.removeKey(panelId);
34810         return panel;
34811     },
34812     
34813     /**
34814      * Returns the panel specified or null if it's not in this region.
34815      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34816      * @return {Roo.ContentPanel}
34817      */
34818     getPanel : function(id){
34819         if(typeof id == "object"){ // must be panel obj
34820             return id;
34821         }
34822         return this.panels.get(id);
34823     },
34824     
34825     /**
34826      * Returns this regions position (north/south/east/west/center).
34827      * @return {String} 
34828      */
34829     getPosition: function(){
34830         return this.position;    
34831     }
34832 });/*
34833  * Based on:
34834  * Ext JS Library 1.1.1
34835  * Copyright(c) 2006-2007, Ext JS, LLC.
34836  *
34837  * Originally Released Under LGPL - original licence link has changed is not relivant.
34838  *
34839  * Fork - LGPL
34840  * <script type="text/javascript">
34841  */
34842  
34843 /**
34844  * @class Roo.bootstrap.layout.Region
34845  * @extends Roo.bootstrap.layout.Basic
34846  * This class represents a region in a layout manager.
34847  
34848  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34849  * @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})
34850  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34851  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34852  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34853  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34854  * @cfg {String}    title           The title for the region (overrides panel titles)
34855  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34856  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34857  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34858  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34859  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34860  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34861  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34862  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34863  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34864  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34865
34866  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34867  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34868  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34869  * @cfg {Number}    width           For East/West panels
34870  * @cfg {Number}    height          For North/South panels
34871  * @cfg {Boolean}   split           To show the splitter
34872  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34873  * 
34874  * @cfg {string}   cls             Extra CSS classes to add to region
34875  * 
34876  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34877  * @cfg {string}   region  the region that it inhabits..
34878  *
34879
34880  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34881  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34882
34883  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34884  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34885  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34886  */
34887 Roo.bootstrap.layout.Region = function(config)
34888 {
34889     this.applyConfig(config);
34890
34891     var mgr = config.mgr;
34892     var pos = config.region;
34893     config.skipConfig = true;
34894     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34895     
34896     if (mgr.el) {
34897         this.onRender(mgr.el);   
34898     }
34899      
34900     this.visible = true;
34901     this.collapsed = false;
34902     this.unrendered_panels = [];
34903 };
34904
34905 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34906
34907     position: '', // set by wrapper (eg. north/south etc..)
34908     unrendered_panels : null,  // unrendered panels.
34909     createBody : function(){
34910         /** This region's body element 
34911         * @type Roo.Element */
34912         this.bodyEl = this.el.createChild({
34913                 tag: "div",
34914                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34915         });
34916     },
34917
34918     onRender: function(ctr, pos)
34919     {
34920         var dh = Roo.DomHelper;
34921         /** This region's container element 
34922         * @type Roo.Element */
34923         this.el = dh.append(ctr.dom, {
34924                 tag: "div",
34925                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34926             }, true);
34927         /** This region's title element 
34928         * @type Roo.Element */
34929     
34930         this.titleEl = dh.append(this.el.dom,
34931             {
34932                     tag: "div",
34933                     unselectable: "on",
34934                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34935                     children:[
34936                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34937                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34938                     ]}, true);
34939         
34940         this.titleEl.enableDisplayMode();
34941         /** This region's title text element 
34942         * @type HTMLElement */
34943         this.titleTextEl = this.titleEl.dom.firstChild;
34944         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34945         /*
34946         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34947         this.closeBtn.enableDisplayMode();
34948         this.closeBtn.on("click", this.closeClicked, this);
34949         this.closeBtn.hide();
34950     */
34951         this.createBody(this.config);
34952         if(this.config.hideWhenEmpty){
34953             this.hide();
34954             this.on("paneladded", this.validateVisibility, this);
34955             this.on("panelremoved", this.validateVisibility, this);
34956         }
34957         if(this.autoScroll){
34958             this.bodyEl.setStyle("overflow", "auto");
34959         }else{
34960             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34961         }
34962         //if(c.titlebar !== false){
34963             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34964                 this.titleEl.hide();
34965             }else{
34966                 this.titleEl.show();
34967                 if(this.config.title){
34968                     this.titleTextEl.innerHTML = this.config.title;
34969                 }
34970             }
34971         //}
34972         if(this.config.collapsed){
34973             this.collapse(true);
34974         }
34975         if(this.config.hidden){
34976             this.hide();
34977         }
34978         
34979         if (this.unrendered_panels && this.unrendered_panels.length) {
34980             for (var i =0;i< this.unrendered_panels.length; i++) {
34981                 this.add(this.unrendered_panels[i]);
34982             }
34983             this.unrendered_panels = null;
34984             
34985         }
34986         
34987     },
34988     
34989     applyConfig : function(c)
34990     {
34991         /*
34992          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34993             var dh = Roo.DomHelper;
34994             if(c.titlebar !== false){
34995                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34996                 this.collapseBtn.on("click", this.collapse, this);
34997                 this.collapseBtn.enableDisplayMode();
34998                 /*
34999                 if(c.showPin === true || this.showPin){
35000                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35001                     this.stickBtn.enableDisplayMode();
35002                     this.stickBtn.on("click", this.expand, this);
35003                     this.stickBtn.hide();
35004                 }
35005                 
35006             }
35007             */
35008             /** This region's collapsed element
35009             * @type Roo.Element */
35010             /*
35011              *
35012             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35013                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35014             ]}, true);
35015             
35016             if(c.floatable !== false){
35017                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35018                this.collapsedEl.on("click", this.collapseClick, this);
35019             }
35020
35021             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35022                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35023                    id: "message", unselectable: "on", style:{"float":"left"}});
35024                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35025              }
35026             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35027             this.expandBtn.on("click", this.expand, this);
35028             
35029         }
35030         
35031         if(this.collapseBtn){
35032             this.collapseBtn.setVisible(c.collapsible == true);
35033         }
35034         
35035         this.cmargins = c.cmargins || this.cmargins ||
35036                          (this.position == "west" || this.position == "east" ?
35037                              {top: 0, left: 2, right:2, bottom: 0} :
35038                              {top: 2, left: 0, right:0, bottom: 2});
35039         */
35040         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35041         
35042         
35043         this.bottomTabs = c.tabPosition != "top";
35044         
35045         this.autoScroll = c.autoScroll || false;
35046         
35047         
35048        
35049         
35050         this.duration = c.duration || .30;
35051         this.slideDuration = c.slideDuration || .45;
35052         this.config = c;
35053        
35054     },
35055     /**
35056      * Returns true if this region is currently visible.
35057      * @return {Boolean}
35058      */
35059     isVisible : function(){
35060         return this.visible;
35061     },
35062
35063     /**
35064      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35065      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35066      */
35067     //setCollapsedTitle : function(title){
35068     //    title = title || "&#160;";
35069      //   if(this.collapsedTitleTextEl){
35070       //      this.collapsedTitleTextEl.innerHTML = title;
35071        // }
35072     //},
35073
35074     getBox : function(){
35075         var b;
35076       //  if(!this.collapsed){
35077             b = this.el.getBox(false, true);
35078        // }else{
35079           //  b = this.collapsedEl.getBox(false, true);
35080         //}
35081         return b;
35082     },
35083
35084     getMargins : function(){
35085         return this.margins;
35086         //return this.collapsed ? this.cmargins : this.margins;
35087     },
35088 /*
35089     highlight : function(){
35090         this.el.addClass("x-layout-panel-dragover");
35091     },
35092
35093     unhighlight : function(){
35094         this.el.removeClass("x-layout-panel-dragover");
35095     },
35096 */
35097     updateBox : function(box)
35098     {
35099         if (!this.bodyEl) {
35100             return; // not rendered yet..
35101         }
35102         
35103         this.box = box;
35104         if(!this.collapsed){
35105             this.el.dom.style.left = box.x + "px";
35106             this.el.dom.style.top = box.y + "px";
35107             this.updateBody(box.width, box.height);
35108         }else{
35109             this.collapsedEl.dom.style.left = box.x + "px";
35110             this.collapsedEl.dom.style.top = box.y + "px";
35111             this.collapsedEl.setSize(box.width, box.height);
35112         }
35113         if(this.tabs){
35114             this.tabs.autoSizeTabs();
35115         }
35116     },
35117
35118     updateBody : function(w, h)
35119     {
35120         if(w !== null){
35121             this.el.setWidth(w);
35122             w -= this.el.getBorderWidth("rl");
35123             if(this.config.adjustments){
35124                 w += this.config.adjustments[0];
35125             }
35126         }
35127         if(h !== null && h > 0){
35128             this.el.setHeight(h);
35129             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35130             h -= this.el.getBorderWidth("tb");
35131             if(this.config.adjustments){
35132                 h += this.config.adjustments[1];
35133             }
35134             this.bodyEl.setHeight(h);
35135             if(this.tabs){
35136                 h = this.tabs.syncHeight(h);
35137             }
35138         }
35139         if(this.panelSize){
35140             w = w !== null ? w : this.panelSize.width;
35141             h = h !== null ? h : this.panelSize.height;
35142         }
35143         if(this.activePanel){
35144             var el = this.activePanel.getEl();
35145             w = w !== null ? w : el.getWidth();
35146             h = h !== null ? h : el.getHeight();
35147             this.panelSize = {width: w, height: h};
35148             this.activePanel.setSize(w, h);
35149         }
35150         if(Roo.isIE && this.tabs){
35151             this.tabs.el.repaint();
35152         }
35153     },
35154
35155     /**
35156      * Returns the container element for this region.
35157      * @return {Roo.Element}
35158      */
35159     getEl : function(){
35160         return this.el;
35161     },
35162
35163     /**
35164      * Hides this region.
35165      */
35166     hide : function(){
35167         //if(!this.collapsed){
35168             this.el.dom.style.left = "-2000px";
35169             this.el.hide();
35170         //}else{
35171          //   this.collapsedEl.dom.style.left = "-2000px";
35172          //   this.collapsedEl.hide();
35173        // }
35174         this.visible = false;
35175         this.fireEvent("visibilitychange", this, false);
35176     },
35177
35178     /**
35179      * Shows this region if it was previously hidden.
35180      */
35181     show : function(){
35182         //if(!this.collapsed){
35183             this.el.show();
35184         //}else{
35185         //    this.collapsedEl.show();
35186        // }
35187         this.visible = true;
35188         this.fireEvent("visibilitychange", this, true);
35189     },
35190 /*
35191     closeClicked : function(){
35192         if(this.activePanel){
35193             this.remove(this.activePanel);
35194         }
35195     },
35196
35197     collapseClick : function(e){
35198         if(this.isSlid){
35199            e.stopPropagation();
35200            this.slideIn();
35201         }else{
35202            e.stopPropagation();
35203            this.slideOut();
35204         }
35205     },
35206 */
35207     /**
35208      * Collapses this region.
35209      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35210      */
35211     /*
35212     collapse : function(skipAnim, skipCheck = false){
35213         if(this.collapsed) {
35214             return;
35215         }
35216         
35217         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35218             
35219             this.collapsed = true;
35220             if(this.split){
35221                 this.split.el.hide();
35222             }
35223             if(this.config.animate && skipAnim !== true){
35224                 this.fireEvent("invalidated", this);
35225                 this.animateCollapse();
35226             }else{
35227                 this.el.setLocation(-20000,-20000);
35228                 this.el.hide();
35229                 this.collapsedEl.show();
35230                 this.fireEvent("collapsed", this);
35231                 this.fireEvent("invalidated", this);
35232             }
35233         }
35234         
35235     },
35236 */
35237     animateCollapse : function(){
35238         // overridden
35239     },
35240
35241     /**
35242      * Expands this region if it was previously collapsed.
35243      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35244      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35245      */
35246     /*
35247     expand : function(e, skipAnim){
35248         if(e) {
35249             e.stopPropagation();
35250         }
35251         if(!this.collapsed || this.el.hasActiveFx()) {
35252             return;
35253         }
35254         if(this.isSlid){
35255             this.afterSlideIn();
35256             skipAnim = true;
35257         }
35258         this.collapsed = false;
35259         if(this.config.animate && skipAnim !== true){
35260             this.animateExpand();
35261         }else{
35262             this.el.show();
35263             if(this.split){
35264                 this.split.el.show();
35265             }
35266             this.collapsedEl.setLocation(-2000,-2000);
35267             this.collapsedEl.hide();
35268             this.fireEvent("invalidated", this);
35269             this.fireEvent("expanded", this);
35270         }
35271     },
35272 */
35273     animateExpand : function(){
35274         // overridden
35275     },
35276
35277     initTabs : function()
35278     {
35279         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35280         
35281         var ts = new Roo.bootstrap.panel.Tabs({
35282                 el: this.bodyEl.dom,
35283                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35284                 disableTooltips: this.config.disableTabTips,
35285                 toolbar : this.config.toolbar
35286             });
35287         
35288         if(this.config.hideTabs){
35289             ts.stripWrap.setDisplayed(false);
35290         }
35291         this.tabs = ts;
35292         ts.resizeTabs = this.config.resizeTabs === true;
35293         ts.minTabWidth = this.config.minTabWidth || 40;
35294         ts.maxTabWidth = this.config.maxTabWidth || 250;
35295         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35296         ts.monitorResize = false;
35297         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35298         ts.bodyEl.addClass('roo-layout-tabs-body');
35299         this.panels.each(this.initPanelAsTab, this);
35300     },
35301
35302     initPanelAsTab : function(panel){
35303         var ti = this.tabs.addTab(
35304             panel.getEl().id,
35305             panel.getTitle(),
35306             null,
35307             this.config.closeOnTab && panel.isClosable(),
35308             panel.tpl
35309         );
35310         if(panel.tabTip !== undefined){
35311             ti.setTooltip(panel.tabTip);
35312         }
35313         ti.on("activate", function(){
35314               this.setActivePanel(panel);
35315         }, this);
35316         
35317         if(this.config.closeOnTab){
35318             ti.on("beforeclose", function(t, e){
35319                 e.cancel = true;
35320                 this.remove(panel);
35321             }, this);
35322         }
35323         
35324         panel.tabItem = ti;
35325         
35326         return ti;
35327     },
35328
35329     updatePanelTitle : function(panel, title)
35330     {
35331         if(this.activePanel == panel){
35332             this.updateTitle(title);
35333         }
35334         if(this.tabs){
35335             var ti = this.tabs.getTab(panel.getEl().id);
35336             ti.setText(title);
35337             if(panel.tabTip !== undefined){
35338                 ti.setTooltip(panel.tabTip);
35339             }
35340         }
35341     },
35342
35343     updateTitle : function(title){
35344         if(this.titleTextEl && !this.config.title){
35345             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35346         }
35347     },
35348
35349     setActivePanel : function(panel)
35350     {
35351         panel = this.getPanel(panel);
35352         if(this.activePanel && this.activePanel != panel){
35353             this.activePanel.setActiveState(false);
35354         }
35355         this.activePanel = panel;
35356         panel.setActiveState(true);
35357         if(this.panelSize){
35358             panel.setSize(this.panelSize.width, this.panelSize.height);
35359         }
35360         if(this.closeBtn){
35361             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35362         }
35363         this.updateTitle(panel.getTitle());
35364         if(this.tabs){
35365             this.fireEvent("invalidated", this);
35366         }
35367         this.fireEvent("panelactivated", this, panel);
35368     },
35369
35370     /**
35371      * Shows the specified panel.
35372      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35373      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35374      */
35375     showPanel : function(panel)
35376     {
35377         panel = this.getPanel(panel);
35378         if(panel){
35379             if(this.tabs){
35380                 var tab = this.tabs.getTab(panel.getEl().id);
35381                 if(tab.isHidden()){
35382                     this.tabs.unhideTab(tab.id);
35383                 }
35384                 tab.activate();
35385             }else{
35386                 this.setActivePanel(panel);
35387             }
35388         }
35389         return panel;
35390     },
35391
35392     /**
35393      * Get the active panel for this region.
35394      * @return {Roo.ContentPanel} The active panel or null
35395      */
35396     getActivePanel : function(){
35397         return this.activePanel;
35398     },
35399
35400     validateVisibility : function(){
35401         if(this.panels.getCount() < 1){
35402             this.updateTitle("&#160;");
35403             this.closeBtn.hide();
35404             this.hide();
35405         }else{
35406             if(!this.isVisible()){
35407                 this.show();
35408             }
35409         }
35410     },
35411
35412     /**
35413      * Adds the passed ContentPanel(s) to this region.
35414      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35415      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35416      */
35417     add : function(panel)
35418     {
35419         if(arguments.length > 1){
35420             for(var i = 0, len = arguments.length; i < len; i++) {
35421                 this.add(arguments[i]);
35422             }
35423             return null;
35424         }
35425         
35426         // if we have not been rendered yet, then we can not really do much of this..
35427         if (!this.bodyEl) {
35428             this.unrendered_panels.push(panel);
35429             return panel;
35430         }
35431         
35432         
35433         
35434         
35435         if(this.hasPanel(panel)){
35436             this.showPanel(panel);
35437             return panel;
35438         }
35439         panel.setRegion(this);
35440         this.panels.add(panel);
35441        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35442             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35443             // and hide them... ???
35444             this.bodyEl.dom.appendChild(panel.getEl().dom);
35445             if(panel.background !== true){
35446                 this.setActivePanel(panel);
35447             }
35448             this.fireEvent("paneladded", this, panel);
35449             return panel;
35450         }
35451         */
35452         if(!this.tabs){
35453             this.initTabs();
35454         }else{
35455             this.initPanelAsTab(panel);
35456         }
35457         
35458         
35459         if(panel.background !== true){
35460             this.tabs.activate(panel.getEl().id);
35461         }
35462         this.fireEvent("paneladded", this, panel);
35463         return panel;
35464     },
35465
35466     /**
35467      * Hides the tab for the specified panel.
35468      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35469      */
35470     hidePanel : function(panel){
35471         if(this.tabs && (panel = this.getPanel(panel))){
35472             this.tabs.hideTab(panel.getEl().id);
35473         }
35474     },
35475
35476     /**
35477      * Unhides the tab for a previously hidden panel.
35478      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35479      */
35480     unhidePanel : function(panel){
35481         if(this.tabs && (panel = this.getPanel(panel))){
35482             this.tabs.unhideTab(panel.getEl().id);
35483         }
35484     },
35485
35486     clearPanels : function(){
35487         while(this.panels.getCount() > 0){
35488              this.remove(this.panels.first());
35489         }
35490     },
35491
35492     /**
35493      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35494      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35495      * @param {Boolean} preservePanel Overrides the config preservePanel option
35496      * @return {Roo.ContentPanel} The panel that was removed
35497      */
35498     remove : function(panel, preservePanel)
35499     {
35500         panel = this.getPanel(panel);
35501         if(!panel){
35502             return null;
35503         }
35504         var e = {};
35505         this.fireEvent("beforeremove", this, panel, e);
35506         if(e.cancel === true){
35507             return null;
35508         }
35509         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35510         var panelId = panel.getId();
35511         this.panels.removeKey(panelId);
35512         if(preservePanel){
35513             document.body.appendChild(panel.getEl().dom);
35514         }
35515         if(this.tabs){
35516             this.tabs.removeTab(panel.getEl().id);
35517         }else if (!preservePanel){
35518             this.bodyEl.dom.removeChild(panel.getEl().dom);
35519         }
35520         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35521             var p = this.panels.first();
35522             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35523             tempEl.appendChild(p.getEl().dom);
35524             this.bodyEl.update("");
35525             this.bodyEl.dom.appendChild(p.getEl().dom);
35526             tempEl = null;
35527             this.updateTitle(p.getTitle());
35528             this.tabs = null;
35529             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35530             this.setActivePanel(p);
35531         }
35532         panel.setRegion(null);
35533         if(this.activePanel == panel){
35534             this.activePanel = null;
35535         }
35536         if(this.config.autoDestroy !== false && preservePanel !== true){
35537             try{panel.destroy();}catch(e){}
35538         }
35539         this.fireEvent("panelremoved", this, panel);
35540         return panel;
35541     },
35542
35543     /**
35544      * Returns the TabPanel component used by this region
35545      * @return {Roo.TabPanel}
35546      */
35547     getTabs : function(){
35548         return this.tabs;
35549     },
35550
35551     createTool : function(parentEl, className){
35552         var btn = Roo.DomHelper.append(parentEl, {
35553             tag: "div",
35554             cls: "x-layout-tools-button",
35555             children: [ {
35556                 tag: "div",
35557                 cls: "roo-layout-tools-button-inner " + className,
35558                 html: "&#160;"
35559             }]
35560         }, true);
35561         btn.addClassOnOver("roo-layout-tools-button-over");
35562         return btn;
35563     }
35564 });/*
35565  * Based on:
35566  * Ext JS Library 1.1.1
35567  * Copyright(c) 2006-2007, Ext JS, LLC.
35568  *
35569  * Originally Released Under LGPL - original licence link has changed is not relivant.
35570  *
35571  * Fork - LGPL
35572  * <script type="text/javascript">
35573  */
35574  
35575
35576
35577 /**
35578  * @class Roo.SplitLayoutRegion
35579  * @extends Roo.LayoutRegion
35580  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35581  */
35582 Roo.bootstrap.layout.Split = function(config){
35583     this.cursor = config.cursor;
35584     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35585 };
35586
35587 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35588 {
35589     splitTip : "Drag to resize.",
35590     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35591     useSplitTips : false,
35592
35593     applyConfig : function(config){
35594         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35595     },
35596     
35597     onRender : function(ctr,pos) {
35598         
35599         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35600         if(!this.config.split){
35601             return;
35602         }
35603         if(!this.split){
35604             
35605             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35606                             tag: "div",
35607                             id: this.el.id + "-split",
35608                             cls: "roo-layout-split roo-layout-split-"+this.position,
35609                             html: "&#160;"
35610             });
35611             /** The SplitBar for this region 
35612             * @type Roo.SplitBar */
35613             // does not exist yet...
35614             Roo.log([this.position, this.orientation]);
35615             
35616             this.split = new Roo.bootstrap.SplitBar({
35617                 dragElement : splitEl,
35618                 resizingElement: this.el,
35619                 orientation : this.orientation
35620             });
35621             
35622             this.split.on("moved", this.onSplitMove, this);
35623             this.split.useShim = this.config.useShim === true;
35624             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35625             if(this.useSplitTips){
35626                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35627             }
35628             //if(config.collapsible){
35629             //    this.split.el.on("dblclick", this.collapse,  this);
35630             //}
35631         }
35632         if(typeof this.config.minSize != "undefined"){
35633             this.split.minSize = this.config.minSize;
35634         }
35635         if(typeof this.config.maxSize != "undefined"){
35636             this.split.maxSize = this.config.maxSize;
35637         }
35638         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35639             this.hideSplitter();
35640         }
35641         
35642     },
35643
35644     getHMaxSize : function(){
35645          var cmax = this.config.maxSize || 10000;
35646          var center = this.mgr.getRegion("center");
35647          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35648     },
35649
35650     getVMaxSize : function(){
35651          var cmax = this.config.maxSize || 10000;
35652          var center = this.mgr.getRegion("center");
35653          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35654     },
35655
35656     onSplitMove : function(split, newSize){
35657         this.fireEvent("resized", this, newSize);
35658     },
35659     
35660     /** 
35661      * Returns the {@link Roo.SplitBar} for this region.
35662      * @return {Roo.SplitBar}
35663      */
35664     getSplitBar : function(){
35665         return this.split;
35666     },
35667     
35668     hide : function(){
35669         this.hideSplitter();
35670         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35671     },
35672
35673     hideSplitter : function(){
35674         if(this.split){
35675             this.split.el.setLocation(-2000,-2000);
35676             this.split.el.hide();
35677         }
35678     },
35679
35680     show : function(){
35681         if(this.split){
35682             this.split.el.show();
35683         }
35684         Roo.bootstrap.layout.Split.superclass.show.call(this);
35685     },
35686     
35687     beforeSlide: function(){
35688         if(Roo.isGecko){// firefox overflow auto bug workaround
35689             this.bodyEl.clip();
35690             if(this.tabs) {
35691                 this.tabs.bodyEl.clip();
35692             }
35693             if(this.activePanel){
35694                 this.activePanel.getEl().clip();
35695                 
35696                 if(this.activePanel.beforeSlide){
35697                     this.activePanel.beforeSlide();
35698                 }
35699             }
35700         }
35701     },
35702     
35703     afterSlide : function(){
35704         if(Roo.isGecko){// firefox overflow auto bug workaround
35705             this.bodyEl.unclip();
35706             if(this.tabs) {
35707                 this.tabs.bodyEl.unclip();
35708             }
35709             if(this.activePanel){
35710                 this.activePanel.getEl().unclip();
35711                 if(this.activePanel.afterSlide){
35712                     this.activePanel.afterSlide();
35713                 }
35714             }
35715         }
35716     },
35717
35718     initAutoHide : function(){
35719         if(this.autoHide !== false){
35720             if(!this.autoHideHd){
35721                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35722                 this.autoHideHd = {
35723                     "mouseout": function(e){
35724                         if(!e.within(this.el, true)){
35725                             st.delay(500);
35726                         }
35727                     },
35728                     "mouseover" : function(e){
35729                         st.cancel();
35730                     },
35731                     scope : this
35732                 };
35733             }
35734             this.el.on(this.autoHideHd);
35735         }
35736     },
35737
35738     clearAutoHide : function(){
35739         if(this.autoHide !== false){
35740             this.el.un("mouseout", this.autoHideHd.mouseout);
35741             this.el.un("mouseover", this.autoHideHd.mouseover);
35742         }
35743     },
35744
35745     clearMonitor : function(){
35746         Roo.get(document).un("click", this.slideInIf, this);
35747     },
35748
35749     // these names are backwards but not changed for compat
35750     slideOut : function(){
35751         if(this.isSlid || this.el.hasActiveFx()){
35752             return;
35753         }
35754         this.isSlid = true;
35755         if(this.collapseBtn){
35756             this.collapseBtn.hide();
35757         }
35758         this.closeBtnState = this.closeBtn.getStyle('display');
35759         this.closeBtn.hide();
35760         if(this.stickBtn){
35761             this.stickBtn.show();
35762         }
35763         this.el.show();
35764         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35765         this.beforeSlide();
35766         this.el.setStyle("z-index", 10001);
35767         this.el.slideIn(this.getSlideAnchor(), {
35768             callback: function(){
35769                 this.afterSlide();
35770                 this.initAutoHide();
35771                 Roo.get(document).on("click", this.slideInIf, this);
35772                 this.fireEvent("slideshow", this);
35773             },
35774             scope: this,
35775             block: true
35776         });
35777     },
35778
35779     afterSlideIn : function(){
35780         this.clearAutoHide();
35781         this.isSlid = false;
35782         this.clearMonitor();
35783         this.el.setStyle("z-index", "");
35784         if(this.collapseBtn){
35785             this.collapseBtn.show();
35786         }
35787         this.closeBtn.setStyle('display', this.closeBtnState);
35788         if(this.stickBtn){
35789             this.stickBtn.hide();
35790         }
35791         this.fireEvent("slidehide", this);
35792     },
35793
35794     slideIn : function(cb){
35795         if(!this.isSlid || this.el.hasActiveFx()){
35796             Roo.callback(cb);
35797             return;
35798         }
35799         this.isSlid = false;
35800         this.beforeSlide();
35801         this.el.slideOut(this.getSlideAnchor(), {
35802             callback: function(){
35803                 this.el.setLeftTop(-10000, -10000);
35804                 this.afterSlide();
35805                 this.afterSlideIn();
35806                 Roo.callback(cb);
35807             },
35808             scope: this,
35809             block: true
35810         });
35811     },
35812     
35813     slideInIf : function(e){
35814         if(!e.within(this.el)){
35815             this.slideIn();
35816         }
35817     },
35818
35819     animateCollapse : function(){
35820         this.beforeSlide();
35821         this.el.setStyle("z-index", 20000);
35822         var anchor = this.getSlideAnchor();
35823         this.el.slideOut(anchor, {
35824             callback : function(){
35825                 this.el.setStyle("z-index", "");
35826                 this.collapsedEl.slideIn(anchor, {duration:.3});
35827                 this.afterSlide();
35828                 this.el.setLocation(-10000,-10000);
35829                 this.el.hide();
35830                 this.fireEvent("collapsed", this);
35831             },
35832             scope: this,
35833             block: true
35834         });
35835     },
35836
35837     animateExpand : function(){
35838         this.beforeSlide();
35839         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35840         this.el.setStyle("z-index", 20000);
35841         this.collapsedEl.hide({
35842             duration:.1
35843         });
35844         this.el.slideIn(this.getSlideAnchor(), {
35845             callback : function(){
35846                 this.el.setStyle("z-index", "");
35847                 this.afterSlide();
35848                 if(this.split){
35849                     this.split.el.show();
35850                 }
35851                 this.fireEvent("invalidated", this);
35852                 this.fireEvent("expanded", this);
35853             },
35854             scope: this,
35855             block: true
35856         });
35857     },
35858
35859     anchors : {
35860         "west" : "left",
35861         "east" : "right",
35862         "north" : "top",
35863         "south" : "bottom"
35864     },
35865
35866     sanchors : {
35867         "west" : "l",
35868         "east" : "r",
35869         "north" : "t",
35870         "south" : "b"
35871     },
35872
35873     canchors : {
35874         "west" : "tl-tr",
35875         "east" : "tr-tl",
35876         "north" : "tl-bl",
35877         "south" : "bl-tl"
35878     },
35879
35880     getAnchor : function(){
35881         return this.anchors[this.position];
35882     },
35883
35884     getCollapseAnchor : function(){
35885         return this.canchors[this.position];
35886     },
35887
35888     getSlideAnchor : function(){
35889         return this.sanchors[this.position];
35890     },
35891
35892     getAlignAdj : function(){
35893         var cm = this.cmargins;
35894         switch(this.position){
35895             case "west":
35896                 return [0, 0];
35897             break;
35898             case "east":
35899                 return [0, 0];
35900             break;
35901             case "north":
35902                 return [0, 0];
35903             break;
35904             case "south":
35905                 return [0, 0];
35906             break;
35907         }
35908     },
35909
35910     getExpandAdj : function(){
35911         var c = this.collapsedEl, cm = this.cmargins;
35912         switch(this.position){
35913             case "west":
35914                 return [-(cm.right+c.getWidth()+cm.left), 0];
35915             break;
35916             case "east":
35917                 return [cm.right+c.getWidth()+cm.left, 0];
35918             break;
35919             case "north":
35920                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35921             break;
35922             case "south":
35923                 return [0, cm.top+cm.bottom+c.getHeight()];
35924             break;
35925         }
35926     }
35927 });/*
35928  * Based on:
35929  * Ext JS Library 1.1.1
35930  * Copyright(c) 2006-2007, Ext JS, LLC.
35931  *
35932  * Originally Released Under LGPL - original licence link has changed is not relivant.
35933  *
35934  * Fork - LGPL
35935  * <script type="text/javascript">
35936  */
35937 /*
35938  * These classes are private internal classes
35939  */
35940 Roo.bootstrap.layout.Center = function(config){
35941     config.region = "center";
35942     Roo.bootstrap.layout.Region.call(this, config);
35943     this.visible = true;
35944     this.minWidth = config.minWidth || 20;
35945     this.minHeight = config.minHeight || 20;
35946 };
35947
35948 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35949     hide : function(){
35950         // center panel can't be hidden
35951     },
35952     
35953     show : function(){
35954         // center panel can't be hidden
35955     },
35956     
35957     getMinWidth: function(){
35958         return this.minWidth;
35959     },
35960     
35961     getMinHeight: function(){
35962         return this.minHeight;
35963     }
35964 });
35965
35966
35967
35968
35969  
35970
35971
35972
35973
35974
35975 Roo.bootstrap.layout.North = function(config)
35976 {
35977     config.region = 'north';
35978     config.cursor = 'n-resize';
35979     
35980     Roo.bootstrap.layout.Split.call(this, config);
35981     
35982     
35983     if(this.split){
35984         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35985         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35986         this.split.el.addClass("roo-layout-split-v");
35987     }
35988     var size = config.initialSize || config.height;
35989     if(typeof size != "undefined"){
35990         this.el.setHeight(size);
35991     }
35992 };
35993 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35994 {
35995     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35996     
35997     
35998     
35999     getBox : function(){
36000         if(this.collapsed){
36001             return this.collapsedEl.getBox();
36002         }
36003         var box = this.el.getBox();
36004         if(this.split){
36005             box.height += this.split.el.getHeight();
36006         }
36007         return box;
36008     },
36009     
36010     updateBox : function(box){
36011         if(this.split && !this.collapsed){
36012             box.height -= this.split.el.getHeight();
36013             this.split.el.setLeft(box.x);
36014             this.split.el.setTop(box.y+box.height);
36015             this.split.el.setWidth(box.width);
36016         }
36017         if(this.collapsed){
36018             this.updateBody(box.width, null);
36019         }
36020         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36021     }
36022 });
36023
36024
36025
36026
36027
36028 Roo.bootstrap.layout.South = function(config){
36029     config.region = 'south';
36030     config.cursor = 's-resize';
36031     Roo.bootstrap.layout.Split.call(this, config);
36032     if(this.split){
36033         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36034         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36035         this.split.el.addClass("roo-layout-split-v");
36036     }
36037     var size = config.initialSize || config.height;
36038     if(typeof size != "undefined"){
36039         this.el.setHeight(size);
36040     }
36041 };
36042
36043 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36044     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36045     getBox : function(){
36046         if(this.collapsed){
36047             return this.collapsedEl.getBox();
36048         }
36049         var box = this.el.getBox();
36050         if(this.split){
36051             var sh = this.split.el.getHeight();
36052             box.height += sh;
36053             box.y -= sh;
36054         }
36055         return box;
36056     },
36057     
36058     updateBox : function(box){
36059         if(this.split && !this.collapsed){
36060             var sh = this.split.el.getHeight();
36061             box.height -= sh;
36062             box.y += sh;
36063             this.split.el.setLeft(box.x);
36064             this.split.el.setTop(box.y-sh);
36065             this.split.el.setWidth(box.width);
36066         }
36067         if(this.collapsed){
36068             this.updateBody(box.width, null);
36069         }
36070         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36071     }
36072 });
36073
36074 Roo.bootstrap.layout.East = function(config){
36075     config.region = "east";
36076     config.cursor = "e-resize";
36077     Roo.bootstrap.layout.Split.call(this, config);
36078     if(this.split){
36079         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36080         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36081         this.split.el.addClass("roo-layout-split-h");
36082     }
36083     var size = config.initialSize || config.width;
36084     if(typeof size != "undefined"){
36085         this.el.setWidth(size);
36086     }
36087 };
36088 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36089     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36090     getBox : function(){
36091         if(this.collapsed){
36092             return this.collapsedEl.getBox();
36093         }
36094         var box = this.el.getBox();
36095         if(this.split){
36096             var sw = this.split.el.getWidth();
36097             box.width += sw;
36098             box.x -= sw;
36099         }
36100         return box;
36101     },
36102
36103     updateBox : function(box){
36104         if(this.split && !this.collapsed){
36105             var sw = this.split.el.getWidth();
36106             box.width -= sw;
36107             this.split.el.setLeft(box.x);
36108             this.split.el.setTop(box.y);
36109             this.split.el.setHeight(box.height);
36110             box.x += sw;
36111         }
36112         if(this.collapsed){
36113             this.updateBody(null, box.height);
36114         }
36115         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36116     }
36117 });
36118
36119 Roo.bootstrap.layout.West = function(config){
36120     config.region = "west";
36121     config.cursor = "w-resize";
36122     
36123     Roo.bootstrap.layout.Split.call(this, config);
36124     if(this.split){
36125         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36126         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36127         this.split.el.addClass("roo-layout-split-h");
36128     }
36129     
36130 };
36131 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36132     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36133     
36134     onRender: function(ctr, pos)
36135     {
36136         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36137         var size = this.config.initialSize || this.config.width;
36138         if(typeof size != "undefined"){
36139             this.el.setWidth(size);
36140         }
36141     },
36142     
36143     getBox : function(){
36144         if(this.collapsed){
36145             return this.collapsedEl.getBox();
36146         }
36147         var box = this.el.getBox();
36148         if(this.split){
36149             box.width += this.split.el.getWidth();
36150         }
36151         return box;
36152     },
36153     
36154     updateBox : function(box){
36155         if(this.split && !this.collapsed){
36156             var sw = this.split.el.getWidth();
36157             box.width -= sw;
36158             this.split.el.setLeft(box.x+box.width);
36159             this.split.el.setTop(box.y);
36160             this.split.el.setHeight(box.height);
36161         }
36162         if(this.collapsed){
36163             this.updateBody(null, box.height);
36164         }
36165         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36166     }
36167 });
36168 Roo.namespace("Roo.bootstrap.panel");/*
36169  * Based on:
36170  * Ext JS Library 1.1.1
36171  * Copyright(c) 2006-2007, Ext JS, LLC.
36172  *
36173  * Originally Released Under LGPL - original licence link has changed is not relivant.
36174  *
36175  * Fork - LGPL
36176  * <script type="text/javascript">
36177  */
36178 /**
36179  * @class Roo.ContentPanel
36180  * @extends Roo.util.Observable
36181  * A basic ContentPanel element.
36182  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36183  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36184  * @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
36185  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36186  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36187  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36188  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36189  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36190  * @cfg {String} title          The title for this panel
36191  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36192  * @cfg {String} url            Calls {@link #setUrl} with this value
36193  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36194  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36195  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36196  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36197  * @cfg {Boolean} badges render the badges
36198
36199  * @constructor
36200  * Create a new ContentPanel.
36201  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36202  * @param {String/Object} config A string to set only the title or a config object
36203  * @param {String} content (optional) Set the HTML content for this panel
36204  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36205  */
36206 Roo.bootstrap.panel.Content = function( config){
36207     
36208     this.tpl = config.tpl || false;
36209     
36210     var el = config.el;
36211     var content = config.content;
36212
36213     if(config.autoCreate){ // xtype is available if this is called from factory
36214         el = Roo.id();
36215     }
36216     this.el = Roo.get(el);
36217     if(!this.el && config && config.autoCreate){
36218         if(typeof config.autoCreate == "object"){
36219             if(!config.autoCreate.id){
36220                 config.autoCreate.id = config.id||el;
36221             }
36222             this.el = Roo.DomHelper.append(document.body,
36223                         config.autoCreate, true);
36224         }else{
36225             var elcfg =  {   tag: "div",
36226                             cls: "roo-layout-inactive-content",
36227                             id: config.id||el
36228                             };
36229             if (config.html) {
36230                 elcfg.html = config.html;
36231                 
36232             }
36233                         
36234             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36235         }
36236     } 
36237     this.closable = false;
36238     this.loaded = false;
36239     this.active = false;
36240    
36241       
36242     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36243         
36244         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36245         
36246         this.wrapEl = this.el; //this.el.wrap();
36247         var ti = [];
36248         if (config.toolbar.items) {
36249             ti = config.toolbar.items ;
36250             delete config.toolbar.items ;
36251         }
36252         
36253         var nitems = [];
36254         this.toolbar.render(this.wrapEl, 'before');
36255         for(var i =0;i < ti.length;i++) {
36256           //  Roo.log(['add child', items[i]]);
36257             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36258         }
36259         this.toolbar.items = nitems;
36260         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36261         delete config.toolbar;
36262         
36263     }
36264     /*
36265     // xtype created footer. - not sure if will work as we normally have to render first..
36266     if (this.footer && !this.footer.el && this.footer.xtype) {
36267         if (!this.wrapEl) {
36268             this.wrapEl = this.el.wrap();
36269         }
36270     
36271         this.footer.container = this.wrapEl.createChild();
36272          
36273         this.footer = Roo.factory(this.footer, Roo);
36274         
36275     }
36276     */
36277     
36278      if(typeof config == "string"){
36279         this.title = config;
36280     }else{
36281         Roo.apply(this, config);
36282     }
36283     
36284     if(this.resizeEl){
36285         this.resizeEl = Roo.get(this.resizeEl, true);
36286     }else{
36287         this.resizeEl = this.el;
36288     }
36289     // handle view.xtype
36290     
36291  
36292     
36293     
36294     this.addEvents({
36295         /**
36296          * @event activate
36297          * Fires when this panel is activated. 
36298          * @param {Roo.ContentPanel} this
36299          */
36300         "activate" : true,
36301         /**
36302          * @event deactivate
36303          * Fires when this panel is activated. 
36304          * @param {Roo.ContentPanel} this
36305          */
36306         "deactivate" : true,
36307
36308         /**
36309          * @event resize
36310          * Fires when this panel is resized if fitToFrame is true.
36311          * @param {Roo.ContentPanel} this
36312          * @param {Number} width The width after any component adjustments
36313          * @param {Number} height The height after any component adjustments
36314          */
36315         "resize" : true,
36316         
36317          /**
36318          * @event render
36319          * Fires when this tab is created
36320          * @param {Roo.ContentPanel} this
36321          */
36322         "render" : true
36323         
36324         
36325         
36326     });
36327     
36328
36329     
36330     
36331     if(this.autoScroll){
36332         this.resizeEl.setStyle("overflow", "auto");
36333     } else {
36334         // fix randome scrolling
36335         //this.el.on('scroll', function() {
36336         //    Roo.log('fix random scolling');
36337         //    this.scrollTo('top',0); 
36338         //});
36339     }
36340     content = content || this.content;
36341     if(content){
36342         this.setContent(content);
36343     }
36344     if(config && config.url){
36345         this.setUrl(this.url, this.params, this.loadOnce);
36346     }
36347     
36348     
36349     
36350     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36351     
36352     if (this.view && typeof(this.view.xtype) != 'undefined') {
36353         this.view.el = this.el.appendChild(document.createElement("div"));
36354         this.view = Roo.factory(this.view); 
36355         this.view.render  &&  this.view.render(false, '');  
36356     }
36357     
36358     
36359     this.fireEvent('render', this);
36360 };
36361
36362 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36363     
36364     tabTip : '',
36365     
36366     setRegion : function(region){
36367         this.region = region;
36368         this.setActiveClass(region && !this.background);
36369     },
36370     
36371     
36372     setActiveClass: function(state)
36373     {
36374         if(state){
36375            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36376            this.el.setStyle('position','relative');
36377         }else{
36378            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36379            this.el.setStyle('position', 'absolute');
36380         } 
36381     },
36382     
36383     /**
36384      * Returns the toolbar for this Panel if one was configured. 
36385      * @return {Roo.Toolbar} 
36386      */
36387     getToolbar : function(){
36388         return this.toolbar;
36389     },
36390     
36391     setActiveState : function(active)
36392     {
36393         this.active = active;
36394         this.setActiveClass(active);
36395         if(!active){
36396             this.fireEvent("deactivate", this);
36397         }else{
36398             this.fireEvent("activate", this);
36399         }
36400     },
36401     /**
36402      * Updates this panel's element
36403      * @param {String} content The new content
36404      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36405     */
36406     setContent : function(content, loadScripts){
36407         this.el.update(content, loadScripts);
36408     },
36409
36410     ignoreResize : function(w, h){
36411         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36412             return true;
36413         }else{
36414             this.lastSize = {width: w, height: h};
36415             return false;
36416         }
36417     },
36418     /**
36419      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36420      * @return {Roo.UpdateManager} The UpdateManager
36421      */
36422     getUpdateManager : function(){
36423         return this.el.getUpdateManager();
36424     },
36425      /**
36426      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36427      * @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:
36428 <pre><code>
36429 panel.load({
36430     url: "your-url.php",
36431     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36432     callback: yourFunction,
36433     scope: yourObject, //(optional scope)
36434     discardUrl: false,
36435     nocache: false,
36436     text: "Loading...",
36437     timeout: 30,
36438     scripts: false
36439 });
36440 </code></pre>
36441      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36442      * 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.
36443      * @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}
36444      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36445      * @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.
36446      * @return {Roo.ContentPanel} this
36447      */
36448     load : function(){
36449         var um = this.el.getUpdateManager();
36450         um.update.apply(um, arguments);
36451         return this;
36452     },
36453
36454
36455     /**
36456      * 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.
36457      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36458      * @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)
36459      * @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)
36460      * @return {Roo.UpdateManager} The UpdateManager
36461      */
36462     setUrl : function(url, params, loadOnce){
36463         if(this.refreshDelegate){
36464             this.removeListener("activate", this.refreshDelegate);
36465         }
36466         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36467         this.on("activate", this.refreshDelegate);
36468         return this.el.getUpdateManager();
36469     },
36470     
36471     _handleRefresh : function(url, params, loadOnce){
36472         if(!loadOnce || !this.loaded){
36473             var updater = this.el.getUpdateManager();
36474             updater.update(url, params, this._setLoaded.createDelegate(this));
36475         }
36476     },
36477     
36478     _setLoaded : function(){
36479         this.loaded = true;
36480     }, 
36481     
36482     /**
36483      * Returns this panel's id
36484      * @return {String} 
36485      */
36486     getId : function(){
36487         return this.el.id;
36488     },
36489     
36490     /** 
36491      * Returns this panel's element - used by regiosn to add.
36492      * @return {Roo.Element} 
36493      */
36494     getEl : function(){
36495         return this.wrapEl || this.el;
36496     },
36497     
36498    
36499     
36500     adjustForComponents : function(width, height)
36501     {
36502         //Roo.log('adjustForComponents ');
36503         if(this.resizeEl != this.el){
36504             width -= this.el.getFrameWidth('lr');
36505             height -= this.el.getFrameWidth('tb');
36506         }
36507         if(this.toolbar){
36508             var te = this.toolbar.getEl();
36509             te.setWidth(width);
36510             height -= te.getHeight();
36511         }
36512         if(this.footer){
36513             var te = this.footer.getEl();
36514             te.setWidth(width);
36515             height -= te.getHeight();
36516         }
36517         
36518         
36519         if(this.adjustments){
36520             width += this.adjustments[0];
36521             height += this.adjustments[1];
36522         }
36523         return {"width": width, "height": height};
36524     },
36525     
36526     setSize : function(width, height){
36527         if(this.fitToFrame && !this.ignoreResize(width, height)){
36528             if(this.fitContainer && this.resizeEl != this.el){
36529                 this.el.setSize(width, height);
36530             }
36531             var size = this.adjustForComponents(width, height);
36532             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36533             this.fireEvent('resize', this, size.width, size.height);
36534         }
36535     },
36536     
36537     /**
36538      * Returns this panel's title
36539      * @return {String} 
36540      */
36541     getTitle : function(){
36542         
36543         if (typeof(this.title) != 'object') {
36544             return this.title;
36545         }
36546         
36547         var t = '';
36548         for (var k in this.title) {
36549             if (!this.title.hasOwnProperty(k)) {
36550                 continue;
36551             }
36552             
36553             if (k.indexOf('-') >= 0) {
36554                 var s = k.split('-');
36555                 for (var i = 0; i<s.length; i++) {
36556                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36557                 }
36558             } else {
36559                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36560             }
36561         }
36562         return t;
36563     },
36564     
36565     /**
36566      * Set this panel's title
36567      * @param {String} title
36568      */
36569     setTitle : function(title){
36570         this.title = title;
36571         if(this.region){
36572             this.region.updatePanelTitle(this, title);
36573         }
36574     },
36575     
36576     /**
36577      * Returns true is this panel was configured to be closable
36578      * @return {Boolean} 
36579      */
36580     isClosable : function(){
36581         return this.closable;
36582     },
36583     
36584     beforeSlide : function(){
36585         this.el.clip();
36586         this.resizeEl.clip();
36587     },
36588     
36589     afterSlide : function(){
36590         this.el.unclip();
36591         this.resizeEl.unclip();
36592     },
36593     
36594     /**
36595      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36596      *   Will fail silently if the {@link #setUrl} method has not been called.
36597      *   This does not activate the panel, just updates its content.
36598      */
36599     refresh : function(){
36600         if(this.refreshDelegate){
36601            this.loaded = false;
36602            this.refreshDelegate();
36603         }
36604     },
36605     
36606     /**
36607      * Destroys this panel
36608      */
36609     destroy : function(){
36610         this.el.removeAllListeners();
36611         var tempEl = document.createElement("span");
36612         tempEl.appendChild(this.el.dom);
36613         tempEl.innerHTML = "";
36614         this.el.remove();
36615         this.el = null;
36616     },
36617     
36618     /**
36619      * form - if the content panel contains a form - this is a reference to it.
36620      * @type {Roo.form.Form}
36621      */
36622     form : false,
36623     /**
36624      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36625      *    This contains a reference to it.
36626      * @type {Roo.View}
36627      */
36628     view : false,
36629     
36630       /**
36631      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36632      * <pre><code>
36633
36634 layout.addxtype({
36635        xtype : 'Form',
36636        items: [ .... ]
36637    }
36638 );
36639
36640 </code></pre>
36641      * @param {Object} cfg Xtype definition of item to add.
36642      */
36643     
36644     
36645     getChildContainer: function () {
36646         return this.getEl();
36647     }
36648     
36649     
36650     /*
36651         var  ret = new Roo.factory(cfg);
36652         return ret;
36653         
36654         
36655         // add form..
36656         if (cfg.xtype.match(/^Form$/)) {
36657             
36658             var el;
36659             //if (this.footer) {
36660             //    el = this.footer.container.insertSibling(false, 'before');
36661             //} else {
36662                 el = this.el.createChild();
36663             //}
36664
36665             this.form = new  Roo.form.Form(cfg);
36666             
36667             
36668             if ( this.form.allItems.length) {
36669                 this.form.render(el.dom);
36670             }
36671             return this.form;
36672         }
36673         // should only have one of theses..
36674         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36675             // views.. should not be just added - used named prop 'view''
36676             
36677             cfg.el = this.el.appendChild(document.createElement("div"));
36678             // factory?
36679             
36680             var ret = new Roo.factory(cfg);
36681              
36682              ret.render && ret.render(false, ''); // render blank..
36683             this.view = ret;
36684             return ret;
36685         }
36686         return false;
36687     }
36688     \*/
36689 });
36690  
36691 /**
36692  * @class Roo.bootstrap.panel.Grid
36693  * @extends Roo.bootstrap.panel.Content
36694  * @constructor
36695  * Create a new GridPanel.
36696  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36697  * @param {Object} config A the config object
36698   
36699  */
36700
36701
36702
36703 Roo.bootstrap.panel.Grid = function(config)
36704 {
36705     
36706       
36707     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36708         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36709
36710     config.el = this.wrapper;
36711     //this.el = this.wrapper;
36712     
36713       if (config.container) {
36714         // ctor'ed from a Border/panel.grid
36715         
36716         
36717         this.wrapper.setStyle("overflow", "hidden");
36718         this.wrapper.addClass('roo-grid-container');
36719
36720     }
36721     
36722     
36723     if(config.toolbar){
36724         var tool_el = this.wrapper.createChild();    
36725         this.toolbar = Roo.factory(config.toolbar);
36726         var ti = [];
36727         if (config.toolbar.items) {
36728             ti = config.toolbar.items ;
36729             delete config.toolbar.items ;
36730         }
36731         
36732         var nitems = [];
36733         this.toolbar.render(tool_el);
36734         for(var i =0;i < ti.length;i++) {
36735           //  Roo.log(['add child', items[i]]);
36736             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36737         }
36738         this.toolbar.items = nitems;
36739         
36740         delete config.toolbar;
36741     }
36742     
36743     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36744     config.grid.scrollBody = true;;
36745     config.grid.monitorWindowResize = false; // turn off autosizing
36746     config.grid.autoHeight = false;
36747     config.grid.autoWidth = false;
36748     
36749     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36750     
36751     if (config.background) {
36752         // render grid on panel activation (if panel background)
36753         this.on('activate', function(gp) {
36754             if (!gp.grid.rendered) {
36755                 gp.grid.render(this.wrapper);
36756                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36757             }
36758         });
36759             
36760     } else {
36761         this.grid.render(this.wrapper);
36762         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36763
36764     }
36765     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36766     // ??? needed ??? config.el = this.wrapper;
36767     
36768     
36769     
36770   
36771     // xtype created footer. - not sure if will work as we normally have to render first..
36772     if (this.footer && !this.footer.el && this.footer.xtype) {
36773         
36774         var ctr = this.grid.getView().getFooterPanel(true);
36775         this.footer.dataSource = this.grid.dataSource;
36776         this.footer = Roo.factory(this.footer, Roo);
36777         this.footer.render(ctr);
36778         
36779     }
36780     
36781     
36782     
36783     
36784      
36785 };
36786
36787 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36788     getId : function(){
36789         return this.grid.id;
36790     },
36791     
36792     /**
36793      * Returns the grid for this panel
36794      * @return {Roo.bootstrap.Table} 
36795      */
36796     getGrid : function(){
36797         return this.grid;    
36798     },
36799     
36800     setSize : function(width, height){
36801         if(!this.ignoreResize(width, height)){
36802             var grid = this.grid;
36803             var size = this.adjustForComponents(width, height);
36804             var gridel = grid.getGridEl();
36805             gridel.setSize(size.width, size.height);
36806             /*
36807             var thd = grid.getGridEl().select('thead',true).first();
36808             var tbd = grid.getGridEl().select('tbody', true).first();
36809             if (tbd) {
36810                 tbd.setSize(width, height - thd.getHeight());
36811             }
36812             */
36813             grid.autoSize();
36814         }
36815     },
36816      
36817     
36818     
36819     beforeSlide : function(){
36820         this.grid.getView().scroller.clip();
36821     },
36822     
36823     afterSlide : function(){
36824         this.grid.getView().scroller.unclip();
36825     },
36826     
36827     destroy : function(){
36828         this.grid.destroy();
36829         delete this.grid;
36830         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36831     }
36832 });
36833
36834 /**
36835  * @class Roo.bootstrap.panel.Nest
36836  * @extends Roo.bootstrap.panel.Content
36837  * @constructor
36838  * Create a new Panel, that can contain a layout.Border.
36839  * 
36840  * 
36841  * @param {Roo.BorderLayout} layout The layout for this panel
36842  * @param {String/Object} config A string to set only the title or a config object
36843  */
36844 Roo.bootstrap.panel.Nest = function(config)
36845 {
36846     // construct with only one argument..
36847     /* FIXME - implement nicer consturctors
36848     if (layout.layout) {
36849         config = layout;
36850         layout = config.layout;
36851         delete config.layout;
36852     }
36853     if (layout.xtype && !layout.getEl) {
36854         // then layout needs constructing..
36855         layout = Roo.factory(layout, Roo);
36856     }
36857     */
36858     
36859     config.el =  config.layout.getEl();
36860     
36861     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36862     
36863     config.layout.monitorWindowResize = false; // turn off autosizing
36864     this.layout = config.layout;
36865     this.layout.getEl().addClass("roo-layout-nested-layout");
36866     
36867     
36868     
36869     
36870 };
36871
36872 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36873
36874     setSize : function(width, height){
36875         if(!this.ignoreResize(width, height)){
36876             var size = this.adjustForComponents(width, height);
36877             var el = this.layout.getEl();
36878             if (size.height < 1) {
36879                 el.setWidth(size.width);   
36880             } else {
36881                 el.setSize(size.width, size.height);
36882             }
36883             var touch = el.dom.offsetWidth;
36884             this.layout.layout();
36885             // ie requires a double layout on the first pass
36886             if(Roo.isIE && !this.initialized){
36887                 this.initialized = true;
36888                 this.layout.layout();
36889             }
36890         }
36891     },
36892     
36893     // activate all subpanels if not currently active..
36894     
36895     setActiveState : function(active){
36896         this.active = active;
36897         this.setActiveClass(active);
36898         
36899         if(!active){
36900             this.fireEvent("deactivate", this);
36901             return;
36902         }
36903         
36904         this.fireEvent("activate", this);
36905         // not sure if this should happen before or after..
36906         if (!this.layout) {
36907             return; // should not happen..
36908         }
36909         var reg = false;
36910         for (var r in this.layout.regions) {
36911             reg = this.layout.getRegion(r);
36912             if (reg.getActivePanel()) {
36913                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36914                 reg.setActivePanel(reg.getActivePanel());
36915                 continue;
36916             }
36917             if (!reg.panels.length) {
36918                 continue;
36919             }
36920             reg.showPanel(reg.getPanel(0));
36921         }
36922         
36923         
36924         
36925         
36926     },
36927     
36928     /**
36929      * Returns the nested BorderLayout for this panel
36930      * @return {Roo.BorderLayout} 
36931      */
36932     getLayout : function(){
36933         return this.layout;
36934     },
36935     
36936      /**
36937      * Adds a xtype elements to the layout of the nested panel
36938      * <pre><code>
36939
36940 panel.addxtype({
36941        xtype : 'ContentPanel',
36942        region: 'west',
36943        items: [ .... ]
36944    }
36945 );
36946
36947 panel.addxtype({
36948         xtype : 'NestedLayoutPanel',
36949         region: 'west',
36950         layout: {
36951            center: { },
36952            west: { }   
36953         },
36954         items : [ ... list of content panels or nested layout panels.. ]
36955    }
36956 );
36957 </code></pre>
36958      * @param {Object} cfg Xtype definition of item to add.
36959      */
36960     addxtype : function(cfg) {
36961         return this.layout.addxtype(cfg);
36962     
36963     }
36964 });        /*
36965  * Based on:
36966  * Ext JS Library 1.1.1
36967  * Copyright(c) 2006-2007, Ext JS, LLC.
36968  *
36969  * Originally Released Under LGPL - original licence link has changed is not relivant.
36970  *
36971  * Fork - LGPL
36972  * <script type="text/javascript">
36973  */
36974 /**
36975  * @class Roo.TabPanel
36976  * @extends Roo.util.Observable
36977  * A lightweight tab container.
36978  * <br><br>
36979  * Usage:
36980  * <pre><code>
36981 // basic tabs 1, built from existing content
36982 var tabs = new Roo.TabPanel("tabs1");
36983 tabs.addTab("script", "View Script");
36984 tabs.addTab("markup", "View Markup");
36985 tabs.activate("script");
36986
36987 // more advanced tabs, built from javascript
36988 var jtabs = new Roo.TabPanel("jtabs");
36989 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36990
36991 // set up the UpdateManager
36992 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36993 var updater = tab2.getUpdateManager();
36994 updater.setDefaultUrl("ajax1.htm");
36995 tab2.on('activate', updater.refresh, updater, true);
36996
36997 // Use setUrl for Ajax loading
36998 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36999 tab3.setUrl("ajax2.htm", null, true);
37000
37001 // Disabled tab
37002 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37003 tab4.disable();
37004
37005 jtabs.activate("jtabs-1");
37006  * </code></pre>
37007  * @constructor
37008  * Create a new TabPanel.
37009  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37010  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37011  */
37012 Roo.bootstrap.panel.Tabs = function(config){
37013     /**
37014     * The container element for this TabPanel.
37015     * @type Roo.Element
37016     */
37017     this.el = Roo.get(config.el);
37018     delete config.el;
37019     if(config){
37020         if(typeof config == "boolean"){
37021             this.tabPosition = config ? "bottom" : "top";
37022         }else{
37023             Roo.apply(this, config);
37024         }
37025     }
37026     
37027     if(this.tabPosition == "bottom"){
37028         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37029         this.el.addClass("roo-tabs-bottom");
37030     }
37031     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37032     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37033     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37034     if(Roo.isIE){
37035         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37036     }
37037     if(this.tabPosition != "bottom"){
37038         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37039          * @type Roo.Element
37040          */
37041         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37042         this.el.addClass("roo-tabs-top");
37043     }
37044     this.items = [];
37045
37046     this.bodyEl.setStyle("position", "relative");
37047
37048     this.active = null;
37049     this.activateDelegate = this.activate.createDelegate(this);
37050
37051     this.addEvents({
37052         /**
37053          * @event tabchange
37054          * Fires when the active tab changes
37055          * @param {Roo.TabPanel} this
37056          * @param {Roo.TabPanelItem} activePanel The new active tab
37057          */
37058         "tabchange": true,
37059         /**
37060          * @event beforetabchange
37061          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37062          * @param {Roo.TabPanel} this
37063          * @param {Object} e Set cancel to true on this object to cancel the tab change
37064          * @param {Roo.TabPanelItem} tab The tab being changed to
37065          */
37066         "beforetabchange" : true
37067     });
37068
37069     Roo.EventManager.onWindowResize(this.onResize, this);
37070     this.cpad = this.el.getPadding("lr");
37071     this.hiddenCount = 0;
37072
37073
37074     // toolbar on the tabbar support...
37075     if (this.toolbar) {
37076         alert("no toolbar support yet");
37077         this.toolbar  = false;
37078         /*
37079         var tcfg = this.toolbar;
37080         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37081         this.toolbar = new Roo.Toolbar(tcfg);
37082         if (Roo.isSafari) {
37083             var tbl = tcfg.container.child('table', true);
37084             tbl.setAttribute('width', '100%');
37085         }
37086         */
37087         
37088     }
37089    
37090
37091
37092     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37093 };
37094
37095 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37096     /*
37097      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37098      */
37099     tabPosition : "top",
37100     /*
37101      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37102      */
37103     currentTabWidth : 0,
37104     /*
37105      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37106      */
37107     minTabWidth : 40,
37108     /*
37109      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37110      */
37111     maxTabWidth : 250,
37112     /*
37113      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37114      */
37115     preferredTabWidth : 175,
37116     /*
37117      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37118      */
37119     resizeTabs : false,
37120     /*
37121      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37122      */
37123     monitorResize : true,
37124     /*
37125      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37126      */
37127     toolbar : false,
37128
37129     /**
37130      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37131      * @param {String} id The id of the div to use <b>or create</b>
37132      * @param {String} text The text for the tab
37133      * @param {String} content (optional) Content to put in the TabPanelItem body
37134      * @param {Boolean} closable (optional) True to create a close icon on the tab
37135      * @return {Roo.TabPanelItem} The created TabPanelItem
37136      */
37137     addTab : function(id, text, content, closable, tpl)
37138     {
37139         var item = new Roo.bootstrap.panel.TabItem({
37140             panel: this,
37141             id : id,
37142             text : text,
37143             closable : closable,
37144             tpl : tpl
37145         });
37146         this.addTabItem(item);
37147         if(content){
37148             item.setContent(content);
37149         }
37150         return item;
37151     },
37152
37153     /**
37154      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37155      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37156      * @return {Roo.TabPanelItem}
37157      */
37158     getTab : function(id){
37159         return this.items[id];
37160     },
37161
37162     /**
37163      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37164      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37165      */
37166     hideTab : function(id){
37167         var t = this.items[id];
37168         if(!t.isHidden()){
37169            t.setHidden(true);
37170            this.hiddenCount++;
37171            this.autoSizeTabs();
37172         }
37173     },
37174
37175     /**
37176      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37177      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37178      */
37179     unhideTab : function(id){
37180         var t = this.items[id];
37181         if(t.isHidden()){
37182            t.setHidden(false);
37183            this.hiddenCount--;
37184            this.autoSizeTabs();
37185         }
37186     },
37187
37188     /**
37189      * Adds an existing {@link Roo.TabPanelItem}.
37190      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37191      */
37192     addTabItem : function(item){
37193         this.items[item.id] = item;
37194         this.items.push(item);
37195       //  if(this.resizeTabs){
37196     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37197   //         this.autoSizeTabs();
37198 //        }else{
37199 //            item.autoSize();
37200        // }
37201     },
37202
37203     /**
37204      * Removes a {@link Roo.TabPanelItem}.
37205      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37206      */
37207     removeTab : function(id){
37208         var items = this.items;
37209         var tab = items[id];
37210         if(!tab) { return; }
37211         var index = items.indexOf(tab);
37212         if(this.active == tab && items.length > 1){
37213             var newTab = this.getNextAvailable(index);
37214             if(newTab) {
37215                 newTab.activate();
37216             }
37217         }
37218         this.stripEl.dom.removeChild(tab.pnode.dom);
37219         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37220             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37221         }
37222         items.splice(index, 1);
37223         delete this.items[tab.id];
37224         tab.fireEvent("close", tab);
37225         tab.purgeListeners();
37226         this.autoSizeTabs();
37227     },
37228
37229     getNextAvailable : function(start){
37230         var items = this.items;
37231         var index = start;
37232         // look for a next tab that will slide over to
37233         // replace the one being removed
37234         while(index < items.length){
37235             var item = items[++index];
37236             if(item && !item.isHidden()){
37237                 return item;
37238             }
37239         }
37240         // if one isn't found select the previous tab (on the left)
37241         index = start;
37242         while(index >= 0){
37243             var item = items[--index];
37244             if(item && !item.isHidden()){
37245                 return item;
37246             }
37247         }
37248         return null;
37249     },
37250
37251     /**
37252      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37253      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37254      */
37255     disableTab : function(id){
37256         var tab = this.items[id];
37257         if(tab && this.active != tab){
37258             tab.disable();
37259         }
37260     },
37261
37262     /**
37263      * Enables a {@link Roo.TabPanelItem} that is disabled.
37264      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37265      */
37266     enableTab : function(id){
37267         var tab = this.items[id];
37268         tab.enable();
37269     },
37270
37271     /**
37272      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37273      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37274      * @return {Roo.TabPanelItem} The TabPanelItem.
37275      */
37276     activate : function(id){
37277         var tab = this.items[id];
37278         if(!tab){
37279             return null;
37280         }
37281         if(tab == this.active || tab.disabled){
37282             return tab;
37283         }
37284         var e = {};
37285         this.fireEvent("beforetabchange", this, e, tab);
37286         if(e.cancel !== true && !tab.disabled){
37287             if(this.active){
37288                 this.active.hide();
37289             }
37290             this.active = this.items[id];
37291             this.active.show();
37292             this.fireEvent("tabchange", this, this.active);
37293         }
37294         return tab;
37295     },
37296
37297     /**
37298      * Gets the active {@link Roo.TabPanelItem}.
37299      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37300      */
37301     getActiveTab : function(){
37302         return this.active;
37303     },
37304
37305     /**
37306      * Updates the tab body element to fit the height of the container element
37307      * for overflow scrolling
37308      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37309      */
37310     syncHeight : function(targetHeight){
37311         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37312         var bm = this.bodyEl.getMargins();
37313         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37314         this.bodyEl.setHeight(newHeight);
37315         return newHeight;
37316     },
37317
37318     onResize : function(){
37319         if(this.monitorResize){
37320             this.autoSizeTabs();
37321         }
37322     },
37323
37324     /**
37325      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37326      */
37327     beginUpdate : function(){
37328         this.updating = true;
37329     },
37330
37331     /**
37332      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37333      */
37334     endUpdate : function(){
37335         this.updating = false;
37336         this.autoSizeTabs();
37337     },
37338
37339     /**
37340      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37341      */
37342     autoSizeTabs : function(){
37343         var count = this.items.length;
37344         var vcount = count - this.hiddenCount;
37345         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37346             return;
37347         }
37348         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37349         var availWidth = Math.floor(w / vcount);
37350         var b = this.stripBody;
37351         if(b.getWidth() > w){
37352             var tabs = this.items;
37353             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37354             if(availWidth < this.minTabWidth){
37355                 /*if(!this.sleft){    // incomplete scrolling code
37356                     this.createScrollButtons();
37357                 }
37358                 this.showScroll();
37359                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37360             }
37361         }else{
37362             if(this.currentTabWidth < this.preferredTabWidth){
37363                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37364             }
37365         }
37366     },
37367
37368     /**
37369      * Returns the number of tabs in this TabPanel.
37370      * @return {Number}
37371      */
37372      getCount : function(){
37373          return this.items.length;
37374      },
37375
37376     /**
37377      * Resizes all the tabs to the passed width
37378      * @param {Number} The new width
37379      */
37380     setTabWidth : function(width){
37381         this.currentTabWidth = width;
37382         for(var i = 0, len = this.items.length; i < len; i++) {
37383                 if(!this.items[i].isHidden()) {
37384                 this.items[i].setWidth(width);
37385             }
37386         }
37387     },
37388
37389     /**
37390      * Destroys this TabPanel
37391      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37392      */
37393     destroy : function(removeEl){
37394         Roo.EventManager.removeResizeListener(this.onResize, this);
37395         for(var i = 0, len = this.items.length; i < len; i++){
37396             this.items[i].purgeListeners();
37397         }
37398         if(removeEl === true){
37399             this.el.update("");
37400             this.el.remove();
37401         }
37402     },
37403     
37404     createStrip : function(container)
37405     {
37406         var strip = document.createElement("nav");
37407         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37408         container.appendChild(strip);
37409         return strip;
37410     },
37411     
37412     createStripList : function(strip)
37413     {
37414         // div wrapper for retard IE
37415         // returns the "tr" element.
37416         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37417         //'<div class="x-tabs-strip-wrap">'+
37418           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37419           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37420         return strip.firstChild; //.firstChild.firstChild.firstChild;
37421     },
37422     createBody : function(container)
37423     {
37424         var body = document.createElement("div");
37425         Roo.id(body, "tab-body");
37426         //Roo.fly(body).addClass("x-tabs-body");
37427         Roo.fly(body).addClass("tab-content");
37428         container.appendChild(body);
37429         return body;
37430     },
37431     createItemBody :function(bodyEl, id){
37432         var body = Roo.getDom(id);
37433         if(!body){
37434             body = document.createElement("div");
37435             body.id = id;
37436         }
37437         //Roo.fly(body).addClass("x-tabs-item-body");
37438         Roo.fly(body).addClass("tab-pane");
37439          bodyEl.insertBefore(body, bodyEl.firstChild);
37440         return body;
37441     },
37442     /** @private */
37443     createStripElements :  function(stripEl, text, closable, tpl)
37444     {
37445         var td = document.createElement("li"); // was td..
37446         
37447         
37448         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37449         
37450         
37451         stripEl.appendChild(td);
37452         /*if(closable){
37453             td.className = "x-tabs-closable";
37454             if(!this.closeTpl){
37455                 this.closeTpl = new Roo.Template(
37456                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37457                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37458                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37459                 );
37460             }
37461             var el = this.closeTpl.overwrite(td, {"text": text});
37462             var close = el.getElementsByTagName("div")[0];
37463             var inner = el.getElementsByTagName("em")[0];
37464             return {"el": el, "close": close, "inner": inner};
37465         } else {
37466         */
37467         // not sure what this is..
37468 //            if(!this.tabTpl){
37469                 //this.tabTpl = new Roo.Template(
37470                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37471                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37472                 //);
37473 //                this.tabTpl = new Roo.Template(
37474 //                   '<a href="#">' +
37475 //                   '<span unselectable="on"' +
37476 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37477 //                            ' >{text}</span></a>'
37478 //                );
37479 //                
37480 //            }
37481
37482
37483             var template = tpl || this.tabTpl || false;
37484             
37485             if(!template){
37486                 
37487                 template = new Roo.Template(
37488                    '<a href="#">' +
37489                    '<span unselectable="on"' +
37490                             (this.disableTooltips ? '' : ' title="{text}"') +
37491                             ' >{text}</span></a>'
37492                 );
37493             }
37494             
37495             switch (typeof(template)) {
37496                 case 'object' :
37497                     break;
37498                 case 'string' :
37499                     template = new Roo.Template(template);
37500                     break;
37501                 default :
37502                     break;
37503             }
37504             
37505             var el = template.overwrite(td, {"text": text});
37506             
37507             var inner = el.getElementsByTagName("span")[0];
37508             
37509             return {"el": el, "inner": inner};
37510             
37511     }
37512         
37513     
37514 });
37515
37516 /**
37517  * @class Roo.TabPanelItem
37518  * @extends Roo.util.Observable
37519  * Represents an individual item (tab plus body) in a TabPanel.
37520  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37521  * @param {String} id The id of this TabPanelItem
37522  * @param {String} text The text for the tab of this TabPanelItem
37523  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37524  */
37525 Roo.bootstrap.panel.TabItem = function(config){
37526     /**
37527      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37528      * @type Roo.TabPanel
37529      */
37530     this.tabPanel = config.panel;
37531     /**
37532      * The id for this TabPanelItem
37533      * @type String
37534      */
37535     this.id = config.id;
37536     /** @private */
37537     this.disabled = false;
37538     /** @private */
37539     this.text = config.text;
37540     /** @private */
37541     this.loaded = false;
37542     this.closable = config.closable;
37543
37544     /**
37545      * The body element for this TabPanelItem.
37546      * @type Roo.Element
37547      */
37548     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37549     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37550     this.bodyEl.setStyle("display", "block");
37551     this.bodyEl.setStyle("zoom", "1");
37552     //this.hideAction();
37553
37554     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37555     /** @private */
37556     this.el = Roo.get(els.el);
37557     this.inner = Roo.get(els.inner, true);
37558     this.textEl = Roo.get(this.el.dom.firstChild, true);
37559     this.pnode = Roo.get(els.el.parentNode, true);
37560     this.el.on("mousedown", this.onTabMouseDown, this);
37561     this.el.on("click", this.onTabClick, this);
37562     /** @private */
37563     if(config.closable){
37564         var c = Roo.get(els.close, true);
37565         c.dom.title = this.closeText;
37566         c.addClassOnOver("close-over");
37567         c.on("click", this.closeClick, this);
37568      }
37569
37570     this.addEvents({
37571          /**
37572          * @event activate
37573          * Fires when this tab becomes the active tab.
37574          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37575          * @param {Roo.TabPanelItem} this
37576          */
37577         "activate": true,
37578         /**
37579          * @event beforeclose
37580          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37581          * @param {Roo.TabPanelItem} this
37582          * @param {Object} e Set cancel to true on this object to cancel the close.
37583          */
37584         "beforeclose": true,
37585         /**
37586          * @event close
37587          * Fires when this tab is closed.
37588          * @param {Roo.TabPanelItem} this
37589          */
37590          "close": true,
37591         /**
37592          * @event deactivate
37593          * Fires when this tab is no longer the active tab.
37594          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37595          * @param {Roo.TabPanelItem} this
37596          */
37597          "deactivate" : true
37598     });
37599     this.hidden = false;
37600
37601     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37602 };
37603
37604 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37605            {
37606     purgeListeners : function(){
37607        Roo.util.Observable.prototype.purgeListeners.call(this);
37608        this.el.removeAllListeners();
37609     },
37610     /**
37611      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37612      */
37613     show : function(){
37614         this.pnode.addClass("active");
37615         this.showAction();
37616         if(Roo.isOpera){
37617             this.tabPanel.stripWrap.repaint();
37618         }
37619         this.fireEvent("activate", this.tabPanel, this);
37620     },
37621
37622     /**
37623      * Returns true if this tab is the active tab.
37624      * @return {Boolean}
37625      */
37626     isActive : function(){
37627         return this.tabPanel.getActiveTab() == this;
37628     },
37629
37630     /**
37631      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37632      */
37633     hide : function(){
37634         this.pnode.removeClass("active");
37635         this.hideAction();
37636         this.fireEvent("deactivate", this.tabPanel, this);
37637     },
37638
37639     hideAction : function(){
37640         this.bodyEl.hide();
37641         this.bodyEl.setStyle("position", "absolute");
37642         this.bodyEl.setLeft("-20000px");
37643         this.bodyEl.setTop("-20000px");
37644     },
37645
37646     showAction : function(){
37647         this.bodyEl.setStyle("position", "relative");
37648         this.bodyEl.setTop("");
37649         this.bodyEl.setLeft("");
37650         this.bodyEl.show();
37651     },
37652
37653     /**
37654      * Set the tooltip for the tab.
37655      * @param {String} tooltip The tab's tooltip
37656      */
37657     setTooltip : function(text){
37658         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37659             this.textEl.dom.qtip = text;
37660             this.textEl.dom.removeAttribute('title');
37661         }else{
37662             this.textEl.dom.title = text;
37663         }
37664     },
37665
37666     onTabClick : function(e){
37667         e.preventDefault();
37668         this.tabPanel.activate(this.id);
37669     },
37670
37671     onTabMouseDown : function(e){
37672         e.preventDefault();
37673         this.tabPanel.activate(this.id);
37674     },
37675 /*
37676     getWidth : function(){
37677         return this.inner.getWidth();
37678     },
37679
37680     setWidth : function(width){
37681         var iwidth = width - this.pnode.getPadding("lr");
37682         this.inner.setWidth(iwidth);
37683         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37684         this.pnode.setWidth(width);
37685     },
37686 */
37687     /**
37688      * Show or hide the tab
37689      * @param {Boolean} hidden True to hide or false to show.
37690      */
37691     setHidden : function(hidden){
37692         this.hidden = hidden;
37693         this.pnode.setStyle("display", hidden ? "none" : "");
37694     },
37695
37696     /**
37697      * Returns true if this tab is "hidden"
37698      * @return {Boolean}
37699      */
37700     isHidden : function(){
37701         return this.hidden;
37702     },
37703
37704     /**
37705      * Returns the text for this tab
37706      * @return {String}
37707      */
37708     getText : function(){
37709         return this.text;
37710     },
37711     /*
37712     autoSize : function(){
37713         //this.el.beginMeasure();
37714         this.textEl.setWidth(1);
37715         /*
37716          *  #2804 [new] Tabs in Roojs
37717          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37718          */
37719         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37720         //this.el.endMeasure();
37721     //},
37722
37723     /**
37724      * Sets the text for the tab (Note: this also sets the tooltip text)
37725      * @param {String} text The tab's text and tooltip
37726      */
37727     setText : function(text){
37728         this.text = text;
37729         this.textEl.update(text);
37730         this.setTooltip(text);
37731         //if(!this.tabPanel.resizeTabs){
37732         //    this.autoSize();
37733         //}
37734     },
37735     /**
37736      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37737      */
37738     activate : function(){
37739         this.tabPanel.activate(this.id);
37740     },
37741
37742     /**
37743      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37744      */
37745     disable : function(){
37746         if(this.tabPanel.active != this){
37747             this.disabled = true;
37748             this.pnode.addClass("disabled");
37749         }
37750     },
37751
37752     /**
37753      * Enables this TabPanelItem if it was previously disabled.
37754      */
37755     enable : function(){
37756         this.disabled = false;
37757         this.pnode.removeClass("disabled");
37758     },
37759
37760     /**
37761      * Sets the content for this TabPanelItem.
37762      * @param {String} content The content
37763      * @param {Boolean} loadScripts true to look for and load scripts
37764      */
37765     setContent : function(content, loadScripts){
37766         this.bodyEl.update(content, loadScripts);
37767     },
37768
37769     /**
37770      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37771      * @return {Roo.UpdateManager} The UpdateManager
37772      */
37773     getUpdateManager : function(){
37774         return this.bodyEl.getUpdateManager();
37775     },
37776
37777     /**
37778      * Set a URL to be used to load the content for this TabPanelItem.
37779      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37780      * @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)
37781      * @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)
37782      * @return {Roo.UpdateManager} The UpdateManager
37783      */
37784     setUrl : function(url, params, loadOnce){
37785         if(this.refreshDelegate){
37786             this.un('activate', this.refreshDelegate);
37787         }
37788         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37789         this.on("activate", this.refreshDelegate);
37790         return this.bodyEl.getUpdateManager();
37791     },
37792
37793     /** @private */
37794     _handleRefresh : function(url, params, loadOnce){
37795         if(!loadOnce || !this.loaded){
37796             var updater = this.bodyEl.getUpdateManager();
37797             updater.update(url, params, this._setLoaded.createDelegate(this));
37798         }
37799     },
37800
37801     /**
37802      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37803      *   Will fail silently if the setUrl method has not been called.
37804      *   This does not activate the panel, just updates its content.
37805      */
37806     refresh : function(){
37807         if(this.refreshDelegate){
37808            this.loaded = false;
37809            this.refreshDelegate();
37810         }
37811     },
37812
37813     /** @private */
37814     _setLoaded : function(){
37815         this.loaded = true;
37816     },
37817
37818     /** @private */
37819     closeClick : function(e){
37820         var o = {};
37821         e.stopEvent();
37822         this.fireEvent("beforeclose", this, o);
37823         if(o.cancel !== true){
37824             this.tabPanel.removeTab(this.id);
37825         }
37826     },
37827     /**
37828      * The text displayed in the tooltip for the close icon.
37829      * @type String
37830      */
37831     closeText : "Close this tab"
37832 });
37833 /*
37834  * - LGPL
37835  *
37836  * element
37837  * 
37838  */
37839
37840 /**
37841  * @class Roo.bootstrap.PhoneInput
37842  * @extends Roo.bootstrap.TriggerField
37843  * Bootstrap PhoneInput class
37844  * 
37845  * @constructor
37846  * Create a new PhoneInput
37847  * @param {Object} config The config object
37848 */
37849
37850 Roo.bootstrap.PhoneInput = function(config){
37851     
37852     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37853     
37854     this.addEvents({
37855         /**
37856          * @event expand
37857          * Fires when the dropdown list is expanded
37858              * @param {Roo.bootstrap.ComboBox} combo This combo box
37859              */
37860         'expand' : true,
37861         /**
37862          * @event collapse
37863          * Fires when the dropdown list is collapsed
37864              * @param {Roo.bootstrap.ComboBox} combo This combo box
37865              */
37866         'collapse' : true,
37867         /**
37868          * @event beforeselect
37869          * Fires before a list item is selected. Return false to cancel the selection.
37870              * @param {Roo.bootstrap.ComboBox} combo This combo box
37871              * @param {Roo.data.Record} record The data record returned from the underlying store
37872              * @param {Number} index The index of the selected item in the dropdown list
37873              */
37874         'beforeselect' : true,
37875         /**
37876          * @event select
37877          * Fires when a list item is selected
37878              * @param {Roo.bootstrap.ComboBox} combo This combo box
37879              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
37880              * @param {Number} index The index of the selected item in the dropdown list
37881              */
37882         'select' : true,
37883         /**
37884          * @event beforequery
37885          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
37886          * The event object passed has these properties:
37887              * @param {Roo.bootstrap.ComboBox} combo This combo box
37888              * @param {String} query The query
37889              * @param {Boolean} forceAll true to force "all" query
37890              * @param {Boolean} cancel true to cancel the query
37891              * @param {Object} e The query event object
37892              */
37893         'beforequery': true,
37894          /**
37895          * @event add
37896          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37897              * @param {Roo.bootstrap.ComboBox} combo This combo box
37898              */
37899         'add' : true,
37900         /**
37901          * @event edit
37902          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37903              * @param {Roo.bootstrap.ComboBox} combo This combo box
37904              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37905              */
37906         'edit' : true,
37907         /**
37908          * @event remove
37909          * Fires when the remove value from the combobox array
37910              * @param {Roo.bootstrap.ComboBox} combo This combo box
37911              */
37912         'remove' : true,
37913         /**
37914          * @event afterremove
37915          * Fires when the remove value from the combobox array
37916              * @param {Roo.bootstrap.ComboBox} combo This combo box
37917              */
37918         'afterremove' : true,
37919         /**
37920          * @event specialfilter
37921          * Fires when specialfilter
37922             * @param {Roo.bootstrap.ComboBox} combo This combo box
37923             */
37924         'touchviewdisplay' : true
37925     });
37926     
37927     this.country = []; //fetch country JSON
37928 };
37929
37930 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37931      
37932      listWidth: undefined,
37933      
37934      modalTitle : '', 
37935      
37936      selectedClass: 'active',
37937      
37938      maxHeight: 300,
37939      
37940      minListWidth : 70,
37941      
37942      triggerAction: 'query',
37943      
37944      validClass : "has-success",
37945      
37946      invalidClass: "has-warning",
37947      
37948      //new settings
37949      defaultCountry: 'hk',
37950      
37951      preferedCountries: undefined, //array
37952      
37953      filterCountries: undefined, //array
37954      
37955      displayMode: undefined, //string
37956      
37957      getAutoCreate : function(){
37958     
37959          this.list = Roo.bootstrap.PhoneInput.List;
37960     
37961         if(this.filterCountries) {
37962             for(var i = 0; i < this.filterCountries.length; i++) {
37963                 delete this.list[this.filterCountries[i]];
37964             }
37965         }
37966         
37967         if (this.preferedCountries) {
37968             //another list??
37969         }
37970         
37971          var align = this.labelAlign || this.parentLabelAlign();
37972          
37973          var id = Roo.id(); //all el??
37974          
37975          var cfg = {
37976              cls: 'form-group'
37977          };
37978          
37979          var input =  {
37980              tag: 'input',
37981              id : id,
37982              type : this.inputType,
37983              cls : 'form-control',
37984              style: 'padding-left: 60px;',
37985              placeholder : this.placeholder || ''
37986          };
37987          
37988          if (this.name) {
37989              input.name = this.name;
37990          }
37991          if (this.size) {
37992              input.cls += ' input-' + this.size;
37993          }
37994          
37995          if (this.disabled) {
37996              input.disabled=true;
37997          }
37998          
37999          var inputblock = input;
38000          
38001          if(this.hasFeedback && !this.allowBlank){
38002              var feedback = {
38003                  tag: 'span',
38004                  cls: 'glyphicon form-control-feedback'
38005              };
38006          }
38007          
38008          inputblock = {
38009              cn :  []
38010          };
38011          
38012          inputblock.cn.push(input);
38013          
38014          if(this.hasFeedback && !this.allowBlank){
38015              inputblock.cls += 'has-feedback';
38016              inputblock.cn.push(feedback);
38017          }
38018          
38019          var box = {
38020              tag: 'div',
38021              cn: [
38022                  {
38023                      tag: 'input',
38024                      type : 'hidden',
38025                      cls: 'form-hidden-field'
38026                  },
38027                  inputblock
38028              ]
38029          };
38030          
38031          var flag = {
38032              tag: 'span',
38033              html: 'flag',
38034              style: 'margin-right:5px',
38035              cls: 'roo-selected-region',
38036              cn: [] //flag position ... (iti-flag-us)
38037          };
38038          
38039          var caret = {
38040              tag: 'span',
38041              cls: 'caret'
38042           };
38043           
38044          if (this.caret != false) {
38045              caret = {
38046                   tag: 'i',
38047                   cls: 'fa fa-' + this.caret
38048              };
38049          }
38050          
38051          var combobox = {
38052              cls: 'roo-select2-container input-group',
38053              cn: []
38054          };
38055          
38056          combobox.cn.push({
38057              tag :'span',
38058              cls : 'input-group-addon btn dropdown-toggle',
38059              style : 'position: absolute; z-index: 4;background: none;border: none; margin-top: 4px; margin-left: 3px; margin-right: 3px;',
38060              cn : [
38061                  flag,
38062                  caret,
38063                  {
38064                      tag: 'span',
38065                      cls: 'combobox-clear',
38066                      cn  : [
38067                          {
38068                              tag : 'i',
38069                              cls: 'icon-remove'
38070                          }
38071                      ]
38072                  }
38073              ]
38074          });
38075          
38076          combobox.cn.push(box);
38077          
38078          if (align ==='left' && this.fieldLabel.length) {
38079              
38080              cfg.cls += ' roo-form-group-label-left';
38081
38082              cfg.cn = [
38083                  {
38084                      tag : 'i',
38085                      cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38086                      tooltip : 'This field is required'
38087                  },
38088                  {
38089                      tag: 'label',
38090                      'for' :  id,
38091                      cls : 'control-label',
38092                      html : this.fieldLabel
38093
38094                  },
38095                  {
38096                      cls : "", 
38097                      cn: [
38098                          combobox
38099                      ]
38100                  }
38101              ];
38102              
38103              var labelCfg = cfg.cn[1];
38104              var contentCfg = cfg.cn[2];
38105              
38106              if(this.indicatorpos == 'right'){
38107                  cfg.cn = [
38108                      {
38109                          tag: 'label',
38110                          'for' :  id,
38111                          cls : 'control-label',
38112                          cn : [
38113                              {
38114                                  tag : 'span',
38115                                  html : this.fieldLabel
38116                              },
38117                              {
38118                                  tag : 'i',
38119                                  cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38120                                  tooltip : 'This field is required'
38121                              }
38122                          ]
38123                      },
38124                      {
38125                          cls : "", 
38126                          cn: [
38127                              combobox
38128                          ]
38129                      }
38130
38131                  ];
38132                  
38133                  labelCfg = cfg.cn[0];
38134                  contentCfg = cfg.cn[1];
38135              }
38136              
38137              if(this.labelWidth > 12){
38138                  labelCfg.style = "width: " + this.labelWidth + 'px';
38139              }
38140              
38141              if(this.labelWidth < 13 && this.labelmd == 0){
38142                  this.labelmd = this.labelWidth;
38143              }
38144              
38145              if(this.labellg > 0){
38146                  labelCfg.cls += ' col-lg-' + this.labellg;
38147                  contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38148              }
38149              
38150              if(this.labelmd > 0){
38151                  labelCfg.cls += ' col-md-' + this.labelmd;
38152                  contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38153              }
38154              
38155              if(this.labelsm > 0){
38156                  labelCfg.cls += ' col-sm-' + this.labelsm;
38157                  contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38158              }
38159              
38160              if(this.labelxs > 0){
38161                  labelCfg.cls += ' col-xs-' + this.labelxs;
38162                  contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38163              }
38164              
38165          } else if ( this.fieldLabel.length) {
38166  //                Roo.log(" label");
38167              cfg.cn = [
38168                  {
38169                     tag : 'i',
38170                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38171                     tooltip : 'This field is required'
38172                 },
38173                 {
38174                     tag: 'label',
38175                     //cls : 'input-group-addon',
38176                     html : this.fieldLabel
38177
38178                 },
38179
38180                 combobox
38181
38182              ];
38183              
38184              if(this.indicatorpos == 'right'){
38185                  
38186                  cfg.cn = [
38187                      {
38188                         tag: 'label',
38189                         cn : [
38190                             {
38191                                 tag : 'span',
38192                                 html : this.fieldLabel
38193                             },
38194                             {
38195                                tag : 'i',
38196                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38197                                tooltip : 'This field is required'
38198                             }
38199                         ]
38200                      },
38201                      combobox
38202                  ];
38203              }
38204          } else {
38205                  cfg = combobox
38206          }
38207          
38208          var settings=this;
38209          ['xs','sm','md','lg'].map(function(size){
38210              if (settings[size]) {
38211                  cfg.cls += ' col-' + size + '-' + settings[size];
38212              }
38213          });
38214          
38215          return cfg;
38216      },
38217      
38218      _initEventsCalled : false,
38219      
38220      initEvents: function()
38221      {   
38222          if (this._initEventsCalled) {
38223              return;
38224          }
38225          
38226          this._initEventsCalled = true;
38227          
38228          this.store =  new Roo.data.SimpleStore({
38229              data : this.list,
38230              fields : ['name','iso','dial_code','order','area_code']
38231          });
38232          
38233          this.store = Roo.factory(this.store, Roo.data);
38234          this.store.parent = this;
38235          
38236          Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38237          
38238          var _this = this;
38239          
38240          (function(){
38241              var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38242              _this.list.setWidth(lw);
38243          }).defer(100);
38244          
38245          this.list.on('mouseover', this.onViewOver, this);
38246          this.list.on('mousemove', this.onViewMove, this);
38247          this.list.on('scroll', this.onViewScroll, this);
38248          
38249          if(!this.tpl){
38250              this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38251          }
38252
38253          this.view = new Roo.View(this.list, this.tpl, {
38254              singleSelect:true, store: this.store, selectedClass: this.selectedClass
38255          });
38256          
38257          this.view.on('click', this.onViewClick, this);
38258          
38259          this.store.on('beforeload', this.onBeforeLoad, this);
38260          this.store.on('load', this.onLoad, this);
38261          this.store.on('loadexception', this.onLoadException, this);
38262          
38263          this.keyNav = new Roo.KeyNav(this.inputEl(), {
38264              "up" : function(e){
38265                  this.inKeyMode = true;
38266                  this.selectPrev();
38267              },
38268
38269              "down" : function(e){
38270                  if(!this.isExpanded()){
38271                      this.onTriggerClick();
38272                  }else{
38273                      this.inKeyMode = true;
38274                      this.selectNext();
38275                  }
38276              },
38277
38278              "enter" : function(e){
38279  //                this.onViewClick();
38280                  //return true;
38281                  this.collapse();
38282                  
38283                  if(this.fireEvent("specialkey", this, e)){
38284                      this.onViewClick(false);
38285                  }
38286                  
38287                  return true;
38288              },
38289
38290              "esc" : function(e){
38291                  this.collapse();
38292              },
38293
38294              "tab" : function(e){
38295                  this.collapse();
38296                  
38297                  if(this.fireEvent("specialkey", this, e)){
38298                      this.onViewClick(false);
38299                  }
38300                  
38301                  return true;
38302              },
38303
38304              scope : this,
38305
38306              doRelay : function(foo, bar, hname){
38307                  if(hname == 'down' || this.scope.isExpanded()){
38308                     return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38309                  }
38310                  return true;
38311              },
38312
38313              forceKeyDown: true
38314          });
38315          
38316     },
38317     
38318     onViewOver : function(e, t){
38319         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38320             return;
38321         }
38322         var item = this.view.findItemFromChild(t);
38323         
38324         if(item){
38325             var index = this.view.indexOf(item);
38326             this.select(index, false);
38327         }
38328     },
38329     
38330     onViewMove : function(e, t){
38331         this.inKeyMode = false;
38332     },
38333     
38334     onViewScroll : function(e, t){
38335         
38336         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){
38337             return;
38338         }
38339         
38340         this.hasQuery = true;
38341         
38342         this.loading = this.list.select('.loading', true).first();
38343         
38344         if(this.loading === null){
38345             this.list.createChild({
38346                 tag: 'div',
38347                 cls: 'loading roo-select2-more-results roo-select2-active',
38348                 html: 'Loading more results...'
38349             });
38350             
38351             this.loading = this.list.select('.loading', true).first();
38352             
38353             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
38354             
38355             this.loading.hide();
38356         }
38357         
38358         this.loading.show();
38359         
38360         var _combo = this;
38361         
38362         this.page++;
38363         this.loadNext = true;
38364         
38365         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
38366         
38367         return;
38368     },
38369     
38370     onTriggerClick : function(e)
38371     {
38372         Roo.log('trigger click');
38373         
38374         if(this.disabled || !this.triggerList){
38375             return;
38376         }
38377         
38378         this.page = 0;
38379         this.loadNext = false;
38380         
38381         if(this.isExpanded()){
38382             this.collapse();
38383             if (!this.blockFocus) {
38384                 this.inputEl().focus();
38385             }
38386             
38387         }else {
38388             this.hasFocus = true;
38389             if(this.triggerAction == 'all') {
38390                 this.doQuery(this.allQuery, true);
38391             } else {
38392                 this.doQuery(this.getRawValue());
38393             }
38394             if (!this.blockFocus) {
38395                 this.inputEl().focus();
38396             }
38397         }
38398     }
38399     
38400  });
38401
38402  Roo.apply(Roo.bootstrap.PhoneInput, {
38403      
38404      /**
38405       * iso2 and abbr for all countries
38406       * @type Object
38407       */
38408      List : [
38409          ["Afghanistan (‫افغانستان‬‎)", "af", "93"],
38410          ["Albania (Shqipëri)", "al", "355"],
38411          ["Algeria (‫الجزائر‬‎)", "dz", "213"],
38412          ["American Samoa", "as", "1684"],
38413          ["Andorra", "ad", "376"],
38414          ["Angola", "ao", "244"],
38415          ["Anguilla", "ai", "1264"],
38416          ["Antigua and Barbuda", "ag", "1268"],
38417          ["Argentina", "ar", "54"],
38418          ["Armenia (Հայաստան)", "am", "374"],
38419          ["Aruba", "aw", "297"],
38420          ["Australia", "au", "61", 0],
38421          ["Austria (Österreich)", "at", "43"],
38422          ["Azerbaijan (Azərbaycan)", "az", "994"],
38423          ["Bahamas", "bs", "1242"],
38424          ["Bahrain (‫البحرين‬‎)", "bh", "973"],
38425          ["Bangladesh (বাংলাদেশ)", "bd", "880"],
38426          ["Barbados", "bb", "1246"],
38427          ["Belarus (Беларусь)", "by", "375"],
38428          ["Belgium (België)", "be", "32"],
38429          ["Belize", "bz", "501"],
38430          ["Benin (Bénin)", "bj", "229"],
38431          ["Bermuda", "bm", "1441"],
38432          ["Bhutan (འབྲུག)", "bt", "975"],
38433          ["Bolivia", "bo", "591"],
38434          ["Bosnia and Herzegovina (Босна и Херцеговина)", "ba", "387"],
38435          ["Botswana", "bw", "267"],
38436          ["Brazil (Brasil)", "br", "55"],
38437          ["British Indian Ocean Territory", "io", "246"],
38438          ["British Virgin Islands", "vg", "1284"],
38439          ["Brunei", "bn", "673"],
38440          ["Bulgaria (България)", "bg", "359"],
38441          ["Burkina Faso", "bf", "226"],
38442          ["Burundi (Uburundi)", "bi", "257"],
38443          ["Cambodia (កម្ពុជា)", "kh", "855"],
38444          ["Cameroon (Cameroun)", "cm", "237"],
38445          ["Canada", "ca", "1", 1, ["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"]],
38446          ["Cape Verde (Kabu Verdi)", "cv", "238"],
38447          ["Caribbean Netherlands", "bq", "599", 1],
38448          ["Cayman Islands", "ky", "1345"],
38449          ["Central African Republic (République centrafricaine)", "cf", "236"],
38450          ["Chad (Tchad)", "td", "235"],
38451          ["Chile", "cl", "56"],
38452          ["China (中国)", "cn", "86"],
38453          ["Christmas Island", "cx", "61", 2],
38454          ["Cocos (Keeling) Islands", "cc", "61", 1],
38455          ["Colombia", "co", "57"],
38456          ["Comoros (‫جزر القمر‬‎)", "km", "269"],
38457          ["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)", "cd", "243"],
38458          ["Congo (Republic) (Congo-Brazzaville)", "cg", "242"],
38459          ["Cook Islands", "ck", "682"],
38460          ["Costa Rica", "cr", "506"],
38461          ["Côte d’Ivoire", "ci", "225"],
38462          ["Croatia (Hrvatska)", "hr", "385"],
38463          ["Cuba", "cu", "53"],
38464          ["Curaçao", "cw", "599", 0],
38465          ["Cyprus (Κύπρος)", "cy", "357"],
38466          ["Czech Republic (Česká republika)", "cz", "420"],
38467          ["Denmark (Danmark)", "dk", "45"],
38468          ["Djibouti", "dj", "253"],
38469          ["Dominica", "dm", "1767"],
38470          ["Dominican Republic (República Dominicana)", "do", "1", 2, ["809", "829", "849"]],
38471          ["Ecuador", "ec", "593"],
38472          ["Egypt (‫مصر‬‎)", "eg", "20"],
38473          ["El Salvador", "sv", "503"],
38474          ["Equatorial Guinea (Guinea Ecuatorial)", "gq", "240"],
38475          ["Eritrea", "er", "291"],
38476          ["Estonia (Eesti)", "ee", "372"],
38477          ["Ethiopia", "et", "251"],
38478          ["Falkland Islands (Islas Malvinas)", "fk", "500"],
38479          ["Faroe Islands (Føroyar)", "fo", "298"],
38480          ["Fiji", "fj", "679"],
38481          ["Finland (Suomi)", "fi", "358", 0],
38482          ["France", "fr", "33"],
38483          ["French Guiana (Guyane française)", "gf", "594"],
38484          ["French Polynesia (Polynésie française)", "pf", "689"],
38485          ["Gabon", "ga", "241"],
38486          ["Gambia", "gm", "220"],
38487          ["Georgia (საქართველო)", "ge", "995"],
38488          ["Germany (Deutschland)", "de", "49"],
38489          ["Ghana (Gaana)", "gh", "233"],
38490          ["Gibraltar", "gi", "350"],
38491          ["Greece (Ελλάδα)", "gr", "30"],
38492          ["Greenland (Kalaallit Nunaat)", "gl", "299"],
38493          ["Grenada", "gd", "1473"],
38494          ["Guadeloupe", "gp", "590", 0],
38495          ["Guam", "gu", "1671"],
38496          ["Guatemala", "gt", "502"],
38497          ["Guernsey", "gg", "44", 1],
38498          ["Guinea (Guinée)", "gn", "224"],
38499          ["Guinea-Bissau (Guiné Bissau)", "gw", "245"],
38500          ["Guyana", "gy", "592"],
38501          ["Haiti", "ht", "509"],
38502          ["Honduras", "hn", "504"],
38503          ["Hong Kong (香港)", "hk", "852"],
38504          ["Hungary (Magyarország)", "hu", "36"],
38505          ["Iceland (Ísland)", "is", "354"],
38506          ["India (भारत)", "in", "91"],
38507          ["Indonesia", "id", "62"],
38508          ["Iran (‫ایران‬‎)", "ir", "98"],
38509          ["Iraq (‫العراق‬‎)", "iq", "964"],
38510          ["Ireland", "ie", "353"],
38511          ["Isle of Man", "im", "44", 2],
38512          ["Israel (‫ישראל‬‎)", "il", "972"],
38513          ["Italy (Italia)", "it", "39", 0],
38514          ["Jamaica", "jm", "1876"],
38515          ["Japan (日本)", "jp", "81"],
38516          ["Jersey", "je", "44", 3],
38517          ["Jordan (‫الأردن‬‎)", "jo", "962"],
38518          ["Kazakhstan (Казахстан)", "kz", "7", 1],
38519          ["Kenya", "ke", "254"],
38520          ["Kiribati", "ki", "686"],
38521          ["Kosovo", "xk", "383"],
38522          ["Kuwait (‫الكويت‬‎)", "kw", "965"],
38523          ["Kyrgyzstan (Кыргызстан)", "kg", "996"],
38524          ["Laos (ລາວ)", "la", "856"],
38525          ["Latvia (Latvija)", "lv", "371"],
38526          ["Lebanon (‫لبنان‬‎)", "lb", "961"],
38527          ["Lesotho", "ls", "266"],
38528          ["Liberia", "lr", "231"],
38529          ["Libya (‫ليبيا‬‎)", "ly", "218"],
38530          ["Liechtenstein", "li", "423"],
38531          ["Lithuania (Lietuva)", "lt", "370"],
38532          ["Luxembourg", "lu", "352"],
38533          ["Macau (澳門)", "mo", "853"],
38534          ["Macedonia (FYROM) (Македонија)", "mk", "389"],
38535          ["Madagascar (Madagasikara)", "mg", "261"],
38536          ["Malawi", "mw", "265"],
38537          ["Malaysia", "my", "60"],
38538          ["Maldives", "mv", "960"],
38539          ["Mali", "ml", "223"],
38540          ["Malta", "mt", "356"],
38541          ["Marshall Islands", "mh", "692"],
38542          ["Martinique", "mq", "596"],
38543          ["Mauritania (‫موريتانيا‬‎)", "mr", "222"],
38544          ["Mauritius (Moris)", "mu", "230"],
38545          ["Mayotte", "yt", "262", 1],
38546          ["Mexico (México)", "mx", "52"],
38547          ["Micronesia", "fm", "691"],
38548          ["Moldova (Republica Moldova)", "md", "373"],
38549          ["Monaco", "mc", "377"],
38550          ["Mongolia (Монгол)", "mn", "976"],
38551          ["Montenegro (Crna Gora)", "me", "382"],
38552          ["Montserrat", "ms", "1664"],
38553          ["Morocco (‫المغرب‬‎)", "ma", "212", 0],
38554          ["Mozambique (Moçambique)", "mz", "258"],
38555          ["Myanmar (Burma) (မြန်မာ)", "mm", "95"],
38556          ["Namibia (Namibië)", "na", "264"],
38557          ["Nauru", "nr", "674"],
38558          ["Nepal (नेपाल)", "np", "977"],
38559          ["Netherlands (Nederland)", "nl", "31"],
38560          ["New Caledonia (Nouvelle-Calédonie)", "nc", "687"],
38561          ["New Zealand", "nz", "64"],
38562          ["Nicaragua", "ni", "505"],
38563          ["Niger (Nijar)", "ne", "227"],
38564          ["Nigeria", "ng", "234"],
38565          ["Niue", "nu", "683"],
38566          ["Norfolk Island", "nf", "672"],
38567          ["North Korea (조선 민주주의 인민 공화국)", "kp", "850"],
38568          ["Northern Mariana Islands", "mp", "1670"],
38569          ["Norway (Norge)", "no", "47", 0],
38570          ["Oman (‫عُمان‬‎)", "om", "968"],
38571          ["Pakistan (‫پاکستان‬‎)", "pk", "92"],
38572          ["Palau", "pw", "680"],
38573          ["Palestine (‫فلسطين‬‎)", "ps", "970"],
38574          ["Panama (Panamá)", "pa", "507"],
38575          ["Papua New Guinea", "pg", "675"],
38576          ["Paraguay", "py", "595"],
38577          ["Peru (Perú)", "pe", "51"],
38578          ["Philippines", "ph", "63"],
38579          ["Poland (Polska)", "pl", "48"],
38580          ["Portugal", "pt", "351"],
38581          ["Puerto Rico", "pr", "1", 3, ["787", "939"]],
38582          ["Qatar (‫قطر‬‎)", "qa", "974"],
38583          ["Réunion (La Réunion)", "re", "262", 0],
38584          ["Romania (România)", "ro", "40"],
38585          ["Russia (Россия)", "ru", "7", 0],
38586          ["Rwanda", "rw", "250"],
38587          ["Saint Barthélemy", "bl", "590", 1],
38588          ["Saint Helena", "sh", "290"],
38589          ["Saint Kitts and Nevis", "kn", "1869"],
38590          ["Saint Lucia", "lc", "1758"],
38591          ["Saint Martin (Saint-Martin (partie française))", "mf", "590", 2],
38592          ["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)", "pm", "508"],
38593          ["Saint Vincent and the Grenadines", "vc", "1784"],
38594          ["Samoa", "ws", "685"],
38595          ["San Marino", "sm", "378"],
38596          ["São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239"],
38597          ["Saudi Arabia (‫المملكة العربية السعودية‬‎)", "sa", "966"],
38598          ["Senegal (Sénégal)", "sn", "221"],
38599          ["Serbia (Србија)", "rs", "381"],
38600          ["Seychelles", "sc", "248"],
38601          ["Sierra Leone", "sl", "232"],
38602          ["Singapore", "sg", "65"],
38603          ["Sint Maarten", "sx", "1721"],
38604          ["Slovakia (Slovensko)", "sk", "421"],
38605          ["Slovenia (Slovenija)", "si", "386"],
38606          ["Solomon Islands", "sb", "677"],
38607          ["Somalia (Soomaaliya)", "so", "252"],
38608          ["South Africa", "za", "27"],
38609          ["South Korea (대한민국)", "kr", "82"],
38610          ["South Sudan (‫جنوب السودان‬‎)", "ss", "211"],
38611          ["Spain (España)", "es", "34"],
38612          ["Sri Lanka (ශ්‍රී ලංකාව)", "lk", "94"],
38613          ["Sudan (‫السودان‬‎)", "sd", "249"],
38614          ["Suriname", "sr", "597"],
38615          ["Svalbard and Jan Mayen", "sj", "47", 1],
38616          ["Swaziland", "sz", "268"],
38617          ["Sweden (Sverige)", "se", "46"],
38618          ["Switzerland (Schweiz)", "ch", "41"],
38619          ["Syria (‫سوريا‬‎)", "sy", "963"],
38620          ["Taiwan (台灣)", "tw", "886"],
38621          ["Tajikistan", "tj", "992"],
38622          ["Tanzania", "tz", "255"],
38623          ["Thailand (ไทย)", "th", "66"],
38624          ["Timor-Leste", "tl", "670"],
38625          ["Togo", "tg", "228"],
38626          ["Tokelau", "tk", "690"],
38627          ["Tonga", "to", "676"],
38628          ["Trinidad and Tobago", "tt", "1868"],
38629          ["Tunisia (‫تونس‬‎)", "tn", "216"],
38630          ["Turkey (Türkiye)", "tr", "90"],
38631          ["Turkmenistan", "tm", "993"],
38632          ["Turks and Caicos Islands", "tc", "1649"],
38633          ["Tuvalu", "tv", "688"],
38634          ["U.S. Virgin Islands", "vi", "1340"],
38635          ["Uganda", "ug", "256"],
38636          ["Ukraine (Україна)", "ua", "380"],
38637          ["United Arab Emirates (‫الإمارات العربية المتحدة‬‎)", "ae", "971"],
38638          ["United Kingdom", "gb", "44", 0],
38639          ["United States", "us", "1", 0],
38640          ["Uruguay", "uy", "598"],
38641          ["Uzbekistan (Oʻzbekiston)", "uz", "998"],
38642          ["Vanuatu", "vu", "678"],
38643          ["Vatican City (Città del Vaticano)", "va", "39", 1],
38644          ["Venezuela", "ve", "58"],
38645          ["Vietnam (Việt Nam)", "vn", "84"],
38646          ["Wallis and Futuna (Wallis-et-Futuna)", "wf", "681"],
38647          ["Western Sahara (‫الصحراء الغربية‬‎)", "eh", "212", 1],
38648          ["Yemen (‫اليمن‬‎)", "ye", "967"],
38649          ["Zambia", "zm", "260"],
38650          ["Zimbabwe", "zw", "263"],
38651          ["Åland Islands", "ax", "358", 1]
38652      ]
38653  });